mirror of
https://github.com/1f349/lavender.git
synced 2024-12-22 07:34:06 +00:00
Allow specific permissions to be sent to authorized clients and use JWT access tokens
This commit is contained in:
parent
05b19e6bf2
commit
c6d64e5d81
@ -15,7 +15,7 @@ type User struct {
|
||||
}
|
||||
|
||||
type ClientInfoDbOutput struct {
|
||||
Sub, Name, Secret, Domain, Owner string
|
||||
Sub, Name, Secret, Domain, Owner, Perms string
|
||||
Public, SSO, Active bool
|
||||
}
|
||||
|
||||
@ -37,3 +37,6 @@ func (c *ClientInfoDbOutput) IsSSO() bool { return c.SSO }
|
||||
|
||||
// IsActive is an extra field for the app manager to get the active state
|
||||
func (c *ClientInfoDbOutput) IsActive() bool { return c.Active }
|
||||
|
||||
// UsePerms is an extra field for the userinfo handler to return user permissions matching the requested values
|
||||
func (c *ClientInfoDbOutput) UsePerms() string { return c.Perms }
|
||||
|
@ -18,6 +18,7 @@ CREATE TABLE IF NOT EXISTS client_store
|
||||
secret TEXT UNIQUE NOT NULL,
|
||||
domain TEXT NOT NULL,
|
||||
owner TEXT NOT NULL,
|
||||
perms TEXT NOT NULL,
|
||||
public INTEGER,
|
||||
sso INTEGER,
|
||||
active INTEGER DEFAULT 1,
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"github.com/1f349/lavender/password"
|
||||
"github.com/go-oauth2/oauth2/v4"
|
||||
"github.com/google/uuid"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -65,8 +66,8 @@ func (t *Tx) GetUserEmail(sub string) (string, error) {
|
||||
|
||||
func (t *Tx) GetClientInfo(sub string) (oauth2.ClientInfo, error) {
|
||||
var u ClientInfoDbOutput
|
||||
row := t.tx.QueryRow(`SELECT secret, name, domain, public, sso, active FROM client_store WHERE subject = ? LIMIT 1`, sub)
|
||||
err := row.Scan(&u.Secret, &u.Name, &u.Domain, &u.Public, &u.SSO, &u.Active)
|
||||
row := t.tx.QueryRow(`SELECT secret, name, domain, perms, public, sso, active FROM client_store WHERE subject = ? LIMIT 1`, sub)
|
||||
err := row.Scan(&u.Secret, &u.Name, &u.Domain, &u.Perms, &u.Public, &u.SSO, &u.Active)
|
||||
u.Owner = sub
|
||||
if !u.Active {
|
||||
return nil, fmt.Errorf("client is not active")
|
||||
@ -76,14 +77,14 @@ func (t *Tx) GetClientInfo(sub string) (oauth2.ClientInfo, error) {
|
||||
|
||||
func (t *Tx) GetAppList(owner string, admin bool, offset int) ([]ClientInfoDbOutput, error) {
|
||||
var u []ClientInfoDbOutput
|
||||
row, err := t.tx.Query(`SELECT subject, name, domain, owner, public, sso, active FROM client_store WHERE owner = ? OR ? = 1 LIMIT 25 OFFSET ?`, owner, admin, offset)
|
||||
row, err := t.tx.Query(`SELECT subject, name, domain, owner, perms, public, sso, active FROM client_store WHERE owner = ? OR ? = 1 LIMIT 25 OFFSET ?`, owner, admin, offset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer row.Close()
|
||||
for row.Next() {
|
||||
var a ClientInfoDbOutput
|
||||
err := row.Scan(&a.Sub, &a.Name, &a.Domain, &a.Owner, &a.Public, &a.SSO, &a.Active)
|
||||
err := row.Scan(&a.Sub, &a.Name, &a.Domain, &a.Owner, &a.Perms, &a.Public, &a.SSO, &a.Active)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -92,18 +93,19 @@ func (t *Tx) GetAppList(owner string, admin bool, offset int) ([]ClientInfoDbOut
|
||||
return u, row.Err()
|
||||
}
|
||||
|
||||
func (t *Tx) InsertClientApp(name, domain string, public, sso, active bool, owner string) error {
|
||||
func (t *Tx) InsertClientApp(name, domain, owner, perms string, public, sso, active bool) error {
|
||||
u := uuid.New()
|
||||
secret, err := password.GenerateApiSecret(70)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = t.tx.Exec(`INSERT INTO client_store (subject, name, secret, domain, owner, public, sso, active) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, u.String(), name, secret, domain, owner, public, sso, active)
|
||||
_, err = t.tx.Exec(`INSERT INTO client_store (subject, name, secret, domain, owner, perms, public, sso, active) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, u.String(), name, secret, domain, owner, perms, public, sso, active)
|
||||
return err
|
||||
}
|
||||
|
||||
func (t *Tx) UpdateClientApp(subject uuid.UUID, owner string, name, domain string, public, sso, active bool) error {
|
||||
_, err := t.tx.Exec(`UPDATE client_store SET name = ?, domain = ?, public = ?, sso = ?, active = ? WHERE subject = ? AND owner = ?`, name, domain, public, sso, active, subject.String(), owner)
|
||||
func (t *Tx) UpdateClientApp(subject uuid.UUID, owner, name, domain, perms string, hasPerms, public, sso, active bool) error {
|
||||
log.Println(hasPerms, perms)
|
||||
_, err := t.tx.Exec(`UPDATE client_store SET name = ?, domain = ?, perms = CASE WHEN ? = true THEN ? ELSE perms END, public = ?, sso = ?, active = ? WHERE subject = ? AND owner = ?`, name, domain, hasPerms, perms, public, sso, active, subject.String(), owner)
|
||||
return err
|
||||
}
|
||||
|
||||
|
3
go.mod
3
go.mod
@ -10,6 +10,7 @@ require (
|
||||
github.com/MrMelon54/exit-reload v0.0.1
|
||||
github.com/go-oauth2/oauth2/v4 v4.5.2
|
||||
github.com/go-session/session v3.1.2+incompatible
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||
github.com/google/subcommands v1.2.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/julienschmidt/httprouter v1.3.0
|
||||
@ -20,9 +21,9 @@ require (
|
||||
|
||||
require (
|
||||
github.com/MrMelon54/rescheduler v0.0.2 // indirect
|
||||
github.com/becheran/wildmatch-go v1.0.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
|
2
go.sum
2
go.sum
@ -15,6 +15,8 @@ github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
||||
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/becheran/wildmatch-go v1.0.0 h1:mE3dGGkTmpKtT4Z+88t8RStG40yN9T+kFEGj2PZFSzA=
|
||||
github.com/becheran/wildmatch-go v1.0.0/go.mod h1:gbMvj0NtVdJ15Mg/mH9uxk2R1QCistMyU7d9KFzroX4=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
|
@ -58,6 +58,12 @@
|
||||
<label for="field_domain">Domain:</label>
|
||||
<input type="text" name="domain" id="field_domain" value="{{.Edit.Domain}}" required/>
|
||||
</div>
|
||||
{{if .IsAdmin}}
|
||||
<div>
|
||||
<label for="field_perms">Perms:</label>
|
||||
<input type="text" name="perms" id="field_perms" value="{{.Edit.Perms}}" required/>
|
||||
</div>
|
||||
{{end}}
|
||||
<div>
|
||||
<label for="field_public">Public: <input type="checkbox" name="public" id="field_public" {{if .Edit.Public}}checked{{end}}/></label>
|
||||
</div>
|
||||
@ -86,6 +92,7 @@
|
||||
<th>ID</th>
|
||||
<th>Name</th>
|
||||
<th>Domain</th>
|
||||
<th>Perms</th>
|
||||
<th>SSO</th>
|
||||
<th>Active</th>
|
||||
<th>Owner</th>
|
||||
@ -98,6 +105,7 @@
|
||||
<td>{{.Sub}}</td>
|
||||
<td>{{.Name}}</td>
|
||||
<td>{{.Domain}}</td>
|
||||
<td>{{.Perms}}</td>
|
||||
<td>{{.SSO}}</td>
|
||||
<td>{{.Active}}</td>
|
||||
<td>{{.Owner}}</td>
|
||||
@ -132,6 +140,12 @@
|
||||
<label for="field_domain">Domain:</label>
|
||||
<input type="text" name="domain" id="field_domain" required/>
|
||||
</div>
|
||||
{{if .IsAdmin}}
|
||||
<div>
|
||||
<label for="field_perms">Perms:</label>
|
||||
<input type="text" name="perms" id="field_perms" required/>
|
||||
</div>
|
||||
{{end}}
|
||||
<div>
|
||||
<label for="field_public">Public: <input type="checkbox" name="public" id="field_public"/></label>
|
||||
</div>
|
||||
|
@ -69,7 +69,7 @@ func (h *HttpServer) OptionalAuthentication(next UserHandler) httprouter.Handle
|
||||
}
|
||||
if auth.IsGuest() {
|
||||
// if this fails internally it just sees the user as logged out
|
||||
h.readLoginDataCookie(rw, req, &auth)
|
||||
h.readLoginDataCookie(req, &auth)
|
||||
}
|
||||
next(rw, req, params, auth)
|
||||
}
|
||||
|
10
server/db.go
10
server/db.go
@ -31,3 +31,13 @@ func (h *HttpServer) DbTx(rw http.ResponseWriter, action func(tx *database.Tx) e
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (h *HttpServer) DbTxRaw(action func(tx *database.Tx) error) bool {
|
||||
return h.DbTx(&fakeRW{}, action)
|
||||
}
|
||||
|
||||
type fakeRW struct{}
|
||||
|
||||
func (f *fakeRW) Header() http.Header { return http.Header{} }
|
||||
func (f *fakeRW) Write(b []byte) (int, error) { return len(b), nil }
|
||||
func (f *fakeRW) WriteHeader(statusCode int) {}
|
||||
|
@ -7,9 +7,8 @@ import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func (h *HttpServer) Home(rw http.ResponseWriter, req *http.Request, _ httprouter.Params, auth UserAuth) {
|
||||
func (h *HttpServer) Home(rw http.ResponseWriter, _ *http.Request, _ httprouter.Params, auth UserAuth) {
|
||||
rw.Header().Set("Content-Type", "text/html")
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
if auth.IsGuest() {
|
||||
pages.RenderPageTemplate(rw, "index-guest", map[string]any{
|
||||
"ServiceName": h.conf.ServiceName,
|
||||
|
57
server/jwt.go
Normal file
57
server/jwt.go
Normal file
@ -0,0 +1,57 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"github.com/1f349/lavender/database"
|
||||
"github.com/1f349/mjwt"
|
||||
"github.com/1f349/mjwt/auth"
|
||||
"github.com/1f349/mjwt/claims"
|
||||
"github.com/go-oauth2/oauth2/v4"
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
"github.com/google/uuid"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type JWTAccessGenerate struct {
|
||||
signer mjwt.Signer
|
||||
db *database.DB
|
||||
}
|
||||
|
||||
func NewJWTAccessGenerate(signer mjwt.Signer, db *database.DB) *JWTAccessGenerate {
|
||||
return &JWTAccessGenerate{signer, db}
|
||||
}
|
||||
|
||||
var _ oauth2.AccessGenerate = &JWTAccessGenerate{}
|
||||
|
||||
func (j *JWTAccessGenerate) Token(ctx context.Context, data *oauth2.GenerateBasic, isGenRefresh bool) (access, refresh string, err error) {
|
||||
beginCtx, err := j.db.BeginCtx(ctx)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
roles, err := beginCtx.GetUserRoles(data.UserID)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
beginCtx.Rollback()
|
||||
|
||||
ps := claims.NewPermStorage()
|
||||
ForEachRole(data.Client.(interface{ UsePerms() string }).UsePerms(), func(role string) {
|
||||
if HasRole(roles, role) {
|
||||
ps.Set(role)
|
||||
}
|
||||
})
|
||||
|
||||
access, err = j.signer.GenerateJwt(data.UserID, "", jwt.ClaimStrings{data.TokenInfo.GetClientID()}, data.TokenInfo.GetAccessExpiresIn(), auth.AccessTokenClaims{
|
||||
Perms: ps,
|
||||
})
|
||||
|
||||
if isGenRefresh {
|
||||
t := uuid.NewHash(sha256.New(), uuid.New(), []byte(access), 5).String()
|
||||
refresh = base64.URLEncoding.EncodeToString([]byte(t))
|
||||
refresh = strings.ToUpper(strings.TrimRight(refresh, "="))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
@ -1,13 +1,12 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"database/sql"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@ -129,7 +128,7 @@ func (h *HttpServer) loginCallback(rw http.ResponseWriter, req *http.Request, _
|
||||
|
||||
// only continues if the above tx succeeds
|
||||
auth.Data = sessionData
|
||||
if auth.SaveSessionData() != nil {
|
||||
if err := auth.SaveSessionData(); err != nil {
|
||||
http.Error(rw, "Failed to save session", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
@ -140,8 +139,8 @@ func (h *HttpServer) loginCallback(rw http.ResponseWriter, req *http.Request, _
|
||||
return
|
||||
}
|
||||
|
||||
if h.setLoginDataCookie(rw, auth.Data.ID, token) {
|
||||
http.Error(rw, "Internal Server Error", http.StatusInternalServerError)
|
||||
if h.setLoginDataCookie(rw, auth.Data.ID) {
|
||||
http.Error(rw, "Failed to save login cookie", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if flowState.redirect != "" {
|
||||
@ -150,22 +149,15 @@ func (h *HttpServer) loginCallback(rw http.ResponseWriter, req *http.Request, _
|
||||
h.SafeRedirect(rw, req)
|
||||
}
|
||||
|
||||
func (h *HttpServer) setLoginDataCookie(rw http.ResponseWriter, userId string, token *oauth2.Token) bool {
|
||||
buf := new(bytes.Buffer)
|
||||
buf.WriteString(userId)
|
||||
buf.WriteByte(0)
|
||||
err := json.NewEncoder(buf).Encode(token)
|
||||
func (h *HttpServer) setLoginDataCookie(rw http.ResponseWriter, userId string) bool {
|
||||
encData, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, h.signingKey.PublicKey(), []byte(userId), []byte("lavender-login-data"))
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
encryptedData, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, h.signingKey.PublicKey(), buf.Bytes(), []byte("lavender-login-data"))
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
encryptedString := base64.RawStdEncoding.EncodeToString(encryptedData)
|
||||
|
||||
http.SetCookie(rw, &http.Cookie{
|
||||
Name: "lavender-login-data",
|
||||
Value: encryptedString,
|
||||
Value: hex.EncodeToString(encData),
|
||||
Path: "/",
|
||||
Expires: time.Now().AddDate(0, 3, 0),
|
||||
Secure: true,
|
||||
@ -174,30 +166,25 @@ func (h *HttpServer) setLoginDataCookie(rw http.ResponseWriter, userId string, t
|
||||
return false
|
||||
}
|
||||
|
||||
func (h *HttpServer) readLoginDataCookie(rw http.ResponseWriter, req *http.Request, u *UserAuth) {
|
||||
func (h *HttpServer) readLoginDataCookie(req *http.Request, u *UserAuth) {
|
||||
loginCookie, err := req.Cookie("lavender-login-data")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
decryptedBytes, err := base64.RawStdEncoding.DecodeString(loginCookie.Value)
|
||||
hexData, err := hex.DecodeString(loginCookie.Value)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
decryptedData, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, h.signingKey.PrivateKey(), decryptedBytes, []byte("lavender-login-data"))
|
||||
decData, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, h.signingKey.PrivateKey(), hexData, []byte("lavender-login-data"))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
buf := bytes.NewBuffer(decryptedData)
|
||||
userId, err := buf.ReadString(0)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
userId = strings.TrimSuffix(userId, "\x00")
|
||||
|
||||
var token *oauth2.Token
|
||||
err = json.NewDecoder(buf).Decode(&token)
|
||||
if err != nil {
|
||||
userId := string(decData)
|
||||
var token oauth2.Token
|
||||
if h.DbTxRaw(func(tx *database.Tx) error {
|
||||
return tx.GetUserToken(userId, &token.AccessToken, &token.RefreshToken, &token.Expiry)
|
||||
}) {
|
||||
return
|
||||
}
|
||||
|
||||
@ -206,11 +193,7 @@ func (h *HttpServer) readLoginDataCookie(rw http.ResponseWriter, req *http.Reque
|
||||
return
|
||||
}
|
||||
|
||||
u.Data, err = h.fetchUserInfo(sso, token)
|
||||
if err != nil {
|
||||
http.Error(rw, "Failed to fetch user info", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
u.Data, _ = h.fetchUserInfo(sso, &token)
|
||||
}
|
||||
|
||||
func (h *HttpServer) fetchUserInfo(sso *issuer.WellKnownOIDC, token *oauth2.Token) (SessionData, error) {
|
||||
|
@ -72,11 +72,12 @@ func (h *HttpServer) ManageAppsPost(rw http.ResponseWriter, req *http.Request, _
|
||||
action := req.Form.Get("action")
|
||||
name := req.Form.Get("name")
|
||||
domain := req.Form.Get("domain")
|
||||
hasPerms := req.Form.Has("perms")
|
||||
public := req.Form.Has("public")
|
||||
sso := req.Form.Has("sso")
|
||||
active := req.Form.Has("active")
|
||||
|
||||
if sso {
|
||||
if sso || hasPerms {
|
||||
var roles string
|
||||
if h.DbTx(rw, func(tx *database.Tx) (err error) {
|
||||
roles, err = tx.GetUserRoles(auth.Data.ID)
|
||||
@ -85,15 +86,19 @@ func (h *HttpServer) ManageAppsPost(rw http.ResponseWriter, req *http.Request, _
|
||||
return
|
||||
}
|
||||
if !HasRole(roles, "lavender:admin") {
|
||||
http.Error(rw, "400 Bad Request: Only admin users can create SSO client applications", http.StatusBadRequest)
|
||||
http.Error(rw, "400 Bad Request: Only admin users can create SSO client applications or edit required permissions", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
var perms string
|
||||
if hasPerms {
|
||||
perms = req.Form.Get("perms")
|
||||
}
|
||||
|
||||
switch action {
|
||||
case "create":
|
||||
if h.DbTx(rw, func(tx *database.Tx) error {
|
||||
return tx.InsertClientApp(name, domain, public, sso, active, auth.Data.ID)
|
||||
return tx.InsertClientApp(name, domain, auth.Data.ID, perms, public, sso, active)
|
||||
}) {
|
||||
return
|
||||
}
|
||||
@ -103,7 +108,7 @@ func (h *HttpServer) ManageAppsPost(rw http.ResponseWriter, req *http.Request, _
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return tx.UpdateClientApp(sub, auth.Data.ID, name, domain, public, sso, active)
|
||||
return tx.UpdateClientApp(sub, auth.Data.ID, name, domain, perms, hasPerms, public, sso, active)
|
||||
}) {
|
||||
return
|
||||
}
|
||||
|
@ -15,3 +15,11 @@ func HasRole(roles, test string) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func ForEachRole(roles string, next func(role string)) {
|
||||
sc := bufio.NewScanner(strings.NewReader(roles))
|
||||
sc.Split(bufio.ScanWords)
|
||||
for sc.Scan() {
|
||||
next(sc.Text())
|
||||
}
|
||||
}
|
||||
|
@ -14,13 +14,12 @@ import (
|
||||
"github.com/1f349/lavender/theme"
|
||||
"github.com/1f349/mjwt"
|
||||
"github.com/go-oauth2/oauth2/v4/errors"
|
||||
"github.com/go-oauth2/oauth2/v4/generates"
|
||||
"github.com/go-oauth2/oauth2/v4/manage"
|
||||
"github.com/go-oauth2/oauth2/v4/server"
|
||||
"github.com/go-oauth2/oauth2/v4/store"
|
||||
"github.com/go-session/session"
|
||||
"github.com/julienschmidt/httprouter"
|
||||
oauth22 "golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@ -84,7 +83,7 @@ func NewHttpServer(conf Conf, db *database.DB, signingKey mjwt.Signer) *http.Ser
|
||||
|
||||
oauthManager.SetAuthorizeCodeTokenCfg(manage.DefaultAuthorizeCodeTokenCfg)
|
||||
oauthManager.MustTokenStorage(store.NewMemoryTokenStore())
|
||||
oauthManager.MapAccessGenerate(generates.NewAccessGenerate())
|
||||
oauthManager.MapAccessGenerate(NewJWTAccessGenerate(hs.signingKey, db))
|
||||
oauthManager.MapClientStorage(clientStore.New(db))
|
||||
|
||||
oauthSrv.SetResponseErrorHandler(func(re *errors.Response) {
|
||||
@ -194,7 +193,7 @@ func NewHttpServer(conf Conf, db *database.DB, signingKey mjwt.Signer) *http.Ser
|
||||
return
|
||||
}
|
||||
|
||||
var clientToken oauth22.Token
|
||||
var clientToken oauth2.Token
|
||||
if hs.DbTx(rw, func(tx *database.Tx) error {
|
||||
return tx.GetUserToken(userId, &clientToken.AccessToken, &clientToken.RefreshToken, &clientToken.Expiry)
|
||||
}) {
|
||||
|
@ -8,12 +8,28 @@
|
||||
|
||||
POP2.init(ssoService + "/authorize", "f4cdb93d-fe28-427b-b037-f03f44c86a16", "openid profile age", 500, 600);
|
||||
|
||||
window.addEventListener("load", function () {
|
||||
doThisThing(false);
|
||||
})
|
||||
|
||||
function updateTokenInfo(data) {
|
||||
document.getElementById("someTextArea").textContent = JSON.stringify(data, null, 2);
|
||||
POP2.getToken(function (x) {
|
||||
document.getElementById("tokenValues").textContent = JSON.stringify(parseJwt(x), null, 2);
|
||||
});
|
||||
}
|
||||
|
||||
function doThisThing() {
|
||||
POP2.clientRequest(ssoService + "/userinfo", {}, true).then(function (x) {
|
||||
function parseJwt(token) {
|
||||
const base64Url = token.split('.')[1];
|
||||
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
|
||||
const jsonPayload = decodeURIComponent(window.atob(base64).split('').map(function (c) {
|
||||
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
|
||||
}).join(''));
|
||||
return JSON.parse(jsonPayload);
|
||||
}
|
||||
|
||||
function doThisThing(popup) {
|
||||
POP2.clientRequest(ssoService + "/userinfo", {}, popup).then(function (x) {
|
||||
return x.json();
|
||||
}).then(function (x) {
|
||||
updateTokenInfo(x);
|
||||
@ -44,7 +60,7 @@
|
||||
</header>
|
||||
<main>
|
||||
<div>
|
||||
<button onclick="doThisThing();">Login</button>
|
||||
<button onclick="doThisThing(true);">Login</button>
|
||||
</div>
|
||||
<div style="display:flex; gap: 2em;">
|
||||
<div>
|
||||
|
@ -104,14 +104,10 @@
|
||||
callbackWaitForToken = undefined;
|
||||
}
|
||||
},
|
||||
// boolean, indicate logged in or not
|
||||
isLoggedIn: function () {
|
||||
return !!access_token;
|
||||
},
|
||||
// pass the access token to callback
|
||||
// if not logged in this triggers login popup;
|
||||
// use isLoggedIn to check login first to prevent popup blocker
|
||||
getToken: function (callback) {
|
||||
getToken: function (callback, popup = true) {
|
||||
if (!client_id || !redirect_uri || !scope) {
|
||||
alert('You need init() first. Check the program flow.');
|
||||
return false;
|
||||
@ -128,8 +124,10 @@
|
||||
w_width,
|
||||
w_height
|
||||
);
|
||||
return false;
|
||||
} else {
|
||||
return callback(access_token);
|
||||
callback(access_token);
|
||||
return true;
|
||||
}
|
||||
},
|
||||
clientRequest: function (resource, options, refresh = false) {
|
||||
@ -159,8 +157,10 @@
|
||||
});
|
||||
};
|
||||
|
||||
if (!refresh) return sendRequest();
|
||||
else {
|
||||
if (!refresh) {
|
||||
if (!access_token) return Promise.reject("missing access token");
|
||||
return sendRequest();
|
||||
} else {
|
||||
return new Promise(function (res, rej) {
|
||||
sendRequest().then(function (x) {
|
||||
res(x);
|
||||
|
Loading…
Reference in New Issue
Block a user