Refactor test client and some other minor changes

This commit is contained in:
Melon 2024-02-22 00:28:13 +00:00
parent 955d600d31
commit 32b02a155d
Signed by: melon
GPG Key ID: 6C9D970C50D26A25
6 changed files with 51 additions and 85 deletions

View File

@ -1,5 +1,5 @@
VITE_SSO_ORIGIN=http://localhost:9090 VITE_SSO_ORIGIN=http://localhost:9090
VITE_OAUTH2_CLIENT_ID=abc123 VITE_OAUTH2_CLIENT_ID=b5a9a8df-827c-4925-b1c1-1940abcf356b
VITE_API_VIOLET=http://localhost:9095/v1/violet VITE_API_VIOLET=http://localhost:9095/v1/violet
VITE_API_ORCHID=http://localhost:9095/v1/orchid VITE_API_ORCHID=http://localhost:9095/v1/orchid

View File

@ -82,6 +82,7 @@ code,
height: 50px; height: 50px;
padding: 4px 16px; padding: 4px 16px;
vertical-align: middle; vertical-align: middle;
box-shadow: 0 4px 8px #0003, 0 6px 20px #00000030;
&:hover { &:hover {
color: black; color: black;
@ -89,7 +90,7 @@ code,
} }
.btn-green { .btn-green {
background-color: #04aa6d; background-color: #209c6f;
} }
table.main-table { table.main-table {

View File

@ -16,12 +16,10 @@ export const LOGIN = {
return POP2.clientRequest(resource, options, refresh); return POP2.clientRequest(resource, options, refresh);
}, },
userinfo: (popup: boolean) => { userinfo: (popup: boolean) => {
console.info("userinfo", popup);
POP2.getToken((token: string) => { POP2.getToken((token: string) => {
POP2.clientRequest(TOKEN_USERINFO_API, {}, popup) POP2.clientRequest(TOKEN_USERINFO_API, {}, popup)
.then(x => x.json()) .then(x => x.json())
.then(x => { .then(x => {
console.log(token, x);
loginStore.set({ loginStore.set({
userinfo: x, userinfo: x,
tokens: {access: token, refresh: ""}, tokens: {access: token, refresh: ""},

View File

@ -32,7 +32,7 @@ export const POP2 = (function (w) {
parseInt(window.location.hash.replace(/^.*expires_in=([^&]+).*$/, "$1")), parseInt(window.location.hash.replace(/^.*expires_in=([^&]+).*$/, "$1")),
); );
} }
if (window.location.search.indexOf("error=")) { if (window.location.search.indexOf("error=") !== -1) {
window.opener.POP2.receiveToken("ERROR"); window.opener.POP2.receiveToken("ERROR");
} }
} }
@ -108,8 +108,8 @@ export const POP2 = (function (w) {
alert("You need init() first. Check the program flow."); alert("You need init() first. Check the program flow.");
return false; return false;
} }
if (access_token == null) {
if (!popup) throw Error("missing access token"); if (!popup) throw Error("missing access token");
if (!access_token) {
callbackWaitForToken = callback; callbackWaitForToken = callback;
popupCenterScreen( popupCenterScreen(
client_endpoint + client_endpoint +
@ -131,7 +131,7 @@ export const POP2 = (function (w) {
} }
}, },
logout: function () { logout: function () {
access_token = ""; access_token = null;
localStorage.removeItem("pop2_access_token"); localStorage.removeItem("pop2_access_token");
}, },
clientRequest: function (resource: RequestInfo, options: RequestInit, refresh = false) { clientRequest: function (resource: RequestInfo, options: RequestInit, refresh = false) {
@ -166,7 +166,7 @@ export const POP2 = (function (w) {
}; };
if (!refresh) { if (!refresh) {
if (!access_token) return Promise.reject("missing access token"); if (access_token == null) return Promise.reject("missing access token");
return sendRequest(); return sendRequest();
} else { } else {
return new Promise(function (res, rej) { return new Promise(function (res, rej) {

View File

@ -1,5 +1,15 @@
<div style="padding:8px;background-color:#bb7900;">Warning: This is currently still under development</div> <div style="padding:8px;background-color:#bb7900;">Warning: This is currently still under development</div>
<div> <div class="button-wrapper">
<a class="btn-green" href="https://uptime-kuma.1f349.com" target="_blank">Status Dashboard</a> <div><a class="btn-green" href="https://uptime-kuma.1f349.com" target="_blank">Status Dashboard</a></div>
<div><a class="btn-green" href="https://sso.1f349.com" target="_blank">SSO Dashboard</a></div>
<div><a class="btn-green" href="https://grafana.1f349.com" target="_blank">Grafana</a></div>
</div> </div>
<style lang="scss">
.button-wrapper {
margin: 20px;
display: flex;
gap: 20px;
}
</style>

View File

@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"log" "log"
"net/http" "net/http"
"net/url"
"strings" "strings"
"time" "time"
@ -32,106 +33,62 @@ func main() {
func ssoServer(signer mjwt.Signer) { func ssoServer(signer mjwt.Signer) {
r := http.NewServeMux() r := http.NewServeMux()
r.HandleFunc("/popup", func(w http.ResponseWriter, r *http.Request) { r.HandleFunc("/authorize", func(w http.ResponseWriter, r *http.Request) {
// request url: http://localhost:9090/authorize?response_type=token&redirect_uri=http://localhost:5173/&scope=openid%20profile%20name&client_id=b5a9a8df-827c-4925-b1c1-1940abcf356b
// redirect url: http://localhost:5173/#access_token=<token>&scope=openid%20profile%20name&token_type=Bearer
if r.FormValue("response_type") != "token" {
panic("invalid response_type")
}
if r.FormValue("redirect_uri") != "http://localhost:5173/" {
panic("invalid redirect_uri")
}
if r.FormValue("scope") != "openid profile name" {
panic("invalid scope")
}
if r.FormValue("client_id") != "b5a9a8df-827c-4925-b1c1-1940abcf356b" {
panic("invalid client_id")
}
ps := claims.NewPermStorage() ps := claims.NewPermStorage()
ps.Set("violet:route") ps.Set("violet:route")
ps.Set("violet:redirect") ps.Set("violet:redirect")
ps.Set("domain:owns=example.com") ps.Set("domain:owns=example.com")
ps.Set("domain:owns=example.org") 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{ accessToken, err := signer.GenerateJwt("81b99bd7-bf74-4cc2-9133-80ed2393dfe6", uuid.NewString(), jwt.ClaimStrings{"b5a9a8df-827c-4925-b1c1-1940abcf356b"}, 15*time.Minute, auth.AccessTokenClaims{
Perms: ps, Perms: ps,
}) })
if err != nil { if err != nil {
http.Error(w, "Failed to generate access token", http.StatusInternalServerError) http.Error(w, "Failed to generate access token", http.StatusInternalServerError)
return return
} }
w.WriteHeader(http.StatusOK) v := url.Values{}
fmt.Fprintf(w, `<!DOCTYPE html> v.Set("access_token", accessToken)
<html lang="en"> v.Set("scope", "openid profile name")
<head> v.Set("token_type", "Bearer")
<title>Test SSO Service</title> v.Set("expires_in", "900")
<script> http.Redirect(w, r, "http://localhost:5173/#"+v.Encode(), http.StatusFound)
let loginData = {
target: "http://localhost:5173",
userinfo: {
"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
},
tokens: {
access: "%s",
refresh: "%s",
},
};
window.addEventListener("load", function () {
setTimeout(function() {
window.opener.postMessage(loginData, loginData.target);
},2000);
});
</script>
</head>
<body>
<header>
<h1>Test SSO Service</h1>
</header>
<main id="mainBody">Loading...</main>
</body>
</html>
`, accessToken, "")
}) })
var corsAccessControl = cors.New(cors.Options{ var corsAccessControl = cors.New(cors.Options{
AllowOriginFunc: func(origin string) bool { AllowOriginFunc: func(origin string) bool {
println(origin)
return origin == "http://localhost:5173" return origin == "http://localhost:5173"
}, },
AllowedMethods: []string{http.MethodPost, http.MethodOptions}, AllowedMethods: []string{http.MethodGet, http.MethodOptions},
AllowedHeaders: []string{"Content-Type"}, AllowedHeaders: []string{"Content-Type", "Authorization"},
AllowCredentials: true, AllowCredentials: true,
}) })
r.HandleFunc("/refresh", func(w http.ResponseWriter, r *http.Request) { r.HandleFunc("/userinfo", func(w http.ResponseWriter, r *http.Request) {
corsAccessControl.ServeHTTP(w, r, 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) w.WriteHeader(http.StatusOK)
_ = json.NewEncoder(w).Encode(map[string]any{ w.Write([]byte(`{"aud":"b5a9a8df-827c-4925-b1c1-1940abcf356b","name":"Test User","picture":"","profile":"http://localhost:9090/user/test-user","sub":"b429562a-20e9-4466-9e8e-bdeb55f2f4a3@localhost","updated_at":1572278406,"website":""}`))
"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)) log.Println("[SSO Server]", http.ListenAndServe(":9090", r))
} }
var serveApiCors = cors.New(cors.Options{ var serveApiCors = cors.New(cors.Options{
AllowedOrigins: []string{"*"}, // allow all origins for api requests AllowOriginFunc: func(origin string) bool {
return origin == "http://localhost:5173"
}, // allow all origins for api requests
AllowedHeaders: []string{"Content-Type", "Authorization"}, AllowedHeaders: []string{"Content-Type", "Authorization"},
AllowedMethods: []string{ AllowedMethods: []string{
http.MethodGet, http.MethodGet,