mirror of
https://github.com/1f349/admin.1f349.com.git
synced 2024-11-12 14:41:34 +00:00
Add apiRequest function to auto refresh outdated access token
This commit is contained in:
parent
3d12a4dda3
commit
47bf91e7ff
35
src/utils/api-request.ts
Normal file
35
src/utils/api-request.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import {get} from "svelte/store";
|
||||
import {getBearer, loginStore} from "../stores/login";
|
||||
|
||||
const TOKEN_REFRESH_API = import.meta.env.VITE_SSO_ORIGIN + "/refresh";
|
||||
|
||||
export async function apiRequest(url: string, init?: RequestInit): Promise<Response> {
|
||||
// setup authorization header
|
||||
if (init == undefined) init = {};
|
||||
init.headers = {...init.headers, Authorization: getBearer()};
|
||||
|
||||
let f = await fetch(url, init);
|
||||
if (f.status !== 403) return f;
|
||||
|
||||
let refreshResp = await fetch(TOKEN_REFRESH_API, {
|
||||
method: "POST",
|
||||
mode: "cors",
|
||||
cache: "no-cache",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({token: get(loginStore)?.tokens.refresh}),
|
||||
});
|
||||
if (refreshResp.status !== 200) {
|
||||
loginStore.set(null);
|
||||
alert("Failed to refresh login session: please login again to continue");
|
||||
throw new Error("403 Unauthorized");
|
||||
}
|
||||
let refreshJson = await refreshResp.json();
|
||||
loginStore.set(refreshJson);
|
||||
|
||||
// update current authorization header
|
||||
init.headers = {...init.headers, Authorization: getBearer()};
|
||||
return await fetch(url, init);
|
||||
}
|
@ -2,6 +2,8 @@
|
||||
import {domainOption} from "../stores/domain-option";
|
||||
import {getBearer} from "../stores/login";
|
||||
import {type Cert, certsTable} from "../stores/certs";
|
||||
import {apiRequest} from "../utils/api-request";
|
||||
import {onMount} from "svelte";
|
||||
|
||||
const apiOrchid = import.meta.env.VITE_API_ORCHID;
|
||||
|
||||
@ -27,25 +29,17 @@
|
||||
return p.endsWith(domain);
|
||||
}
|
||||
|
||||
let promiseForTable: Promise<void> = Object.entries($certsTable).length === 0 ? reloadTable() : Promise.resolve();
|
||||
let promiseForTable: Promise<void> = Object.entries($certsTable).length === 0 ? reloadTable() : reloadTable();
|
||||
|
||||
function reloadTable(): Promise<void> {
|
||||
return new Promise<void>((res, rej) => {
|
||||
fetch(apiOrchid + "/owned", {headers: {Authorization: getBearer()}})
|
||||
.then(x => {
|
||||
if (x.status !== 200) throw new Error("Unexpected status code: " + x.status);
|
||||
return x.json();
|
||||
})
|
||||
.then(x => {
|
||||
let rows = x as Map<number, Cert>;
|
||||
Object.values(rows).forEach(x => {
|
||||
$certsTable[Object(x.id).toString()] = x;
|
||||
});
|
||||
console.log($certsTable);
|
||||
res();
|
||||
})
|
||||
.catch(x => rej(x));
|
||||
async function reloadTable(): Promise<void> {
|
||||
let f = await apiRequest(apiOrchid + "/owned");
|
||||
if (f.status !== 200) throw new Error("Unexpected status code: " + f.status);
|
||||
let fJson = await f.json();
|
||||
let rows = fJson as Map<number, Cert>;
|
||||
Object.values(rows).forEach(x => {
|
||||
$certsTable[Object(x.id).toString()] = x;
|
||||
});
|
||||
console.log($certsTable);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -1,5 +1,29 @@
|
||||
<script lang="ts">
|
||||
import {loginStore, parseJwt} from "../stores/login";
|
||||
|
||||
let tokenExp = 0;
|
||||
let diffExp = 0;
|
||||
|
||||
loginStore.subscribe(x => {
|
||||
if (!x) return;
|
||||
let jwt = parseJwt(x?.tokens.access);
|
||||
tokenExp = jwt.exp;
|
||||
});
|
||||
|
||||
function timeDiff(exp: number) {
|
||||
if (exp === 0) return 0;
|
||||
return exp * 1000 - new Date().getTime();
|
||||
}
|
||||
|
||||
setInterval(() => {
|
||||
diffExp = timeDiff(tokenExp);
|
||||
}, 500);
|
||||
</script>
|
||||
|
||||
<div style="padding:8px;background-color:#bb7900;">Warning: This is currently still under development</div>
|
||||
|
||||
<div>
|
||||
<a class="btn-green" href="https://uptime-kuma.1f349.com" target="_blank">Status Dashboard</a>
|
||||
</div>
|
||||
|
||||
<div>{diffExp === 0 ? "No token" : diffExp}</div>
|
||||
|
@ -2,6 +2,7 @@
|
||||
import {domainOption} from "../stores/domain-option";
|
||||
import {getBearer} from "../stores/login";
|
||||
import {type Site, sitesTable} from "../stores/sites";
|
||||
import {apiRequest} from "../utils/api-request";
|
||||
|
||||
const apiSiteHosting = import.meta.env.VITE_API_SITE_HOSTING;
|
||||
|
||||
@ -22,51 +23,33 @@
|
||||
|
||||
let promiseForTable: Promise<void> = Object.entries($sitesTable).length === 0 ? reloadTable() : Promise.resolve();
|
||||
|
||||
function reloadTable(): Promise<void> {
|
||||
return new Promise<void>((res, rej) => {
|
||||
fetch(apiSiteHosting, {headers: {Authorization: getBearer()}})
|
||||
.then(x => {
|
||||
if (x.status !== 200) throw new Error("Unexpected status code: " + x.status);
|
||||
return x.json();
|
||||
})
|
||||
.then(x => {
|
||||
let rows = x as Site[];
|
||||
rows.forEach(x => {
|
||||
$sitesTable[x.domain] = x;
|
||||
});
|
||||
res();
|
||||
})
|
||||
.catch(x => rej(x));
|
||||
async function reloadTable(): Promise<void> {
|
||||
let f = await apiRequest(apiSiteHosting);
|
||||
if (f.status !== 200) throw new Error("Unexpected status code: " + f.status);
|
||||
let fJson = await f.json();
|
||||
let rows = fJson as Site[];
|
||||
rows.forEach(x => {
|
||||
$sitesTable[x.domain] = x;
|
||||
});
|
||||
}
|
||||
|
||||
function deleteBranch(site: Site, branch: string) {
|
||||
fetch(apiSiteHosting, {
|
||||
async function deleteBranch(site: Site, branch: string) {
|
||||
let f = await apiRequest(apiSiteHosting, {
|
||||
method: "POST",
|
||||
headers: {Authorization: getBearer()},
|
||||
body: JSON.stringify({submit: "delete-branch", site: site.domain, branch}),
|
||||
})
|
||||
.then(x => {
|
||||
if (x.status !== 200) throw new Error("Unexpected status code: " + x.status);
|
||||
promiseForTable = reloadTable();
|
||||
})
|
||||
.catch(x => alert("Error deleting branch: " + x));
|
||||
});
|
||||
if (f.status !== 200) throw new Error("Unexpected status code: " + f.status);
|
||||
promiseForTable = reloadTable();
|
||||
}
|
||||
|
||||
function resetSiteSecret(site: Site) {
|
||||
fetch(apiSiteHosting, {
|
||||
async function resetSiteSecret(site: Site) {
|
||||
let f = await apiRequest(apiSiteHosting, {
|
||||
method: "POST",
|
||||
headers: {Authorization: getBearer()},
|
||||
body: JSON.stringify({submit: "secret", site: site.domain}),
|
||||
})
|
||||
.then(x => {
|
||||
if (x.status !== 200) throw new Error("Unexpected status code: " + x.status);
|
||||
return x.json();
|
||||
})
|
||||
.then(x => {
|
||||
alert("New secret: " + x.secret);
|
||||
})
|
||||
.catch(x => alert("Error resetting secret: " + x));
|
||||
});
|
||||
if (f.status !== 200) throw new Error("Unexpected status code: " + f.status);
|
||||
let fJson = await f.json();
|
||||
alert("New secret: " + fJson.secret);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -5,6 +5,8 @@
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import {apiRequest} from "../utils/api-request";
|
||||
|
||||
import {writable, type Writable} from "svelte/store";
|
||||
|
||||
import {getBearer} from "../stores/login";
|
||||
@ -39,30 +41,23 @@
|
||||
|
||||
let promiseForTable: Promise<void> = Object.keys($tableData).length === 0 ? reloadTable() : Promise.resolve();
|
||||
|
||||
function reloadTable(): Promise<void> {
|
||||
return new Promise<void>((res, rej) => {
|
||||
fetch(apiUrl, {headers: {Authorization: getBearer()}})
|
||||
.then(x => {
|
||||
if (x.status !== 200) throw new Error("Unexpected status code: " + x.status);
|
||||
return x.json();
|
||||
})
|
||||
.then(x => {
|
||||
let rows = x as T[];
|
||||
let srcs = new Set(Object.keys($tableData));
|
||||
rows.forEach(x => {
|
||||
$tableData[x.src] = {
|
||||
client: !$tableData[x.src] ? JSON.parse(JSON.stringify(x)) : $tableData[x.src]?.client,
|
||||
server: x,
|
||||
p: Promise.resolve(),
|
||||
};
|
||||
srcs.delete(x.src);
|
||||
});
|
||||
srcs.forEach(x => {
|
||||
$tableData[x].server = null;
|
||||
});
|
||||
res();
|
||||
})
|
||||
.catch(x => rej(x));
|
||||
async function reloadTable(): Promise<void> {
|
||||
let f = await apiRequest(apiUrl);
|
||||
if (f.status !== 200) throw new Error("Unexpected status code: " + f.status);
|
||||
let fJson = await f.json();
|
||||
|
||||
let rows = fJson as T[];
|
||||
let srcs = new Set(Object.keys($tableData));
|
||||
rows.forEach(x => {
|
||||
$tableData[x.src] = {
|
||||
client: !$tableData[x.src] ? JSON.parse(JSON.stringify(x)) : $tableData[x.src]?.client,
|
||||
server: x,
|
||||
p: Promise.resolve(),
|
||||
};
|
||||
srcs.delete(x.src);
|
||||
});
|
||||
srcs.forEach(x => {
|
||||
$tableData[x].server = null;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@ require (
|
||||
require github.com/becheran/wildmatch-go v1.0.0 // indirect
|
||||
|
||||
require (
|
||||
github.com/1f349/mjwt v0.2.0
|
||||
github.com/1f349/mjwt v0.2.1
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
@ -1,5 +1,7 @@
|
||||
github.com/1f349/mjwt v0.2.0 h1:1c3+J05RRBsClGxA91SzT3I2DkwasGA4OgLcIeXWmq4=
|
||||
github.com/1f349/mjwt v0.2.0/go.mod h1:KEs6jd9JjWrQW+8feP2pGAU7pdA3aYTqjkT/YQr73PU=
|
||||
github.com/1f349/mjwt v0.2.1 h1:REdiM/MaNjYQwHvI39LaMPhlvMg4Vy9SgomWMsKTNz8=
|
||||
github.com/1f349/mjwt v0.2.1/go.mod h1:KEs6jd9JjWrQW+8feP2pGAU7pdA3aYTqjkT/YQr73PU=
|
||||
github.com/MrMelon54/mjwt v0.1.1 h1:m+aTpxbhQCrOPKHN170DQMFR5r938LkviU38unob5Jw=
|
||||
github.com/MrMelon54/mjwt v0.1.1/go.mod h1:oYrDBWK09Hju98xb+bRQ0wy+RuAzacxYvKYOZchR2Tk=
|
||||
github.com/becheran/wildmatch-go v1.0.0 h1:mE3dGGkTmpKtT4Z+88t8RStG40yN9T+kFEGj2PZFSzA=
|
||||
|
@ -38,7 +38,7 @@ func ssoServer(signer mjwt.Signer) {
|
||||
ps.Set("violet:redirect")
|
||||
ps.Set("domain:owns=example.com")
|
||||
ps.Set("domain:owns=example.org")
|
||||
accessToken, err := signer.GenerateJwt("81b99bd7-bf74-4cc2-9133-80ed2393dfe6", uuid.NewString(), jwt.ClaimStrings{"d0555671-df9d-42d0-a4d6-94b694251f0b"}, 15*time.Minute, auth.AccessTokenClaims{
|
||||
accessToken, err := signer.GenerateJwt("81b99bd7-bf74-4cc2-9133-80ed2393dfe6", uuid.NewString(), jwt.ClaimStrings{"d0555671-df9d-42d0-a4d6-94b694251f0b"}, 10*time.Second, auth.AccessTokenClaims{
|
||||
Perms: ps,
|
||||
})
|
||||
if err != nil {
|
||||
@ -84,6 +84,49 @@ func ssoServer(signer mjwt.Signer) {
|
||||
</html>
|
||||
`, accessToken, "")
|
||||
})
|
||||
var corsAccessControl = cors.New(cors.Options{
|
||||
AllowOriginFunc: func(origin string) bool {
|
||||
println(origin)
|
||||
return origin == "http://localhost:5173"
|
||||
},
|
||||
AllowedMethods: []string{http.MethodPost, http.MethodOptions},
|
||||
AllowedHeaders: []string{"Content-Type"},
|
||||
AllowCredentials: true,
|
||||
})
|
||||
r.HandleFunc("/refresh", func(w http.ResponseWriter, r *http.Request) {
|
||||
corsAccessControl.ServeHTTP(w, r, func(w http.ResponseWriter, r *http.Request) {
|
||||
ps := claims.NewPermStorage()
|
||||
ps.Set("violet:route")
|
||||
ps.Set("violet:redirect")
|
||||
ps.Set("domain:owns=example.com")
|
||||
ps.Set("domain:owns=example.org")
|
||||
accessToken, err := signer.GenerateJwt("81b99bd7-bf74-4cc2-9133-80ed2393dfe6", uuid.NewString(), jwt.ClaimStrings{"d0555671-df9d-42d0-a4d6-94b694251f0b"}, 10*time.Second, auth.AccessTokenClaims{
|
||||
Perms: ps,
|
||||
})
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to generate access token", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{
|
||||
"target": "http://localhost:5173",
|
||||
"tokens": map[string]any{
|
||||
"access": accessToken,
|
||||
"refresh": "",
|
||||
},
|
||||
"userinfo": map[string]any{
|
||||
"aud": "d0555671-df9d-42d0-a4d6-94b694251f0b",
|
||||
"email": "admin@localhost",
|
||||
"email_verified": true,
|
||||
"name": "Admin",
|
||||
"preferred_username": "admin",
|
||||
"sub": "81b99bd7-bf74-4cc2-9133-80ed2393dfe6",
|
||||
"picture": "http://localhost:5173/1f349.svg",
|
||||
"updated_at": 0,
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
log.Println("[SSO Server]", http.ListenAndServe(":9090", r))
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user