split oauth_tokens.claims column up; improved fonts; allow multiple sse connections per user; make navbar and use friendly

This commit is contained in:
2026-01-23 02:31:34 -07:00
parent 38aa3796b9
commit 3d70d08dac
16 changed files with 1703 additions and 69 deletions
+40 -13
View File
@@ -2,7 +2,6 @@ package authentication
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/url"
@@ -131,8 +130,6 @@ func (a *Authenticator) Exchange(ctx context.Context, state, code string) (acces
return "", "", time.Time{}, err
}
claimsJSON, _ := json.Marshal(claims)
// store the token, and the potentially new user
if _, err := a.db.Exec(
@@ -171,7 +168,12 @@ func (a *Authenticator) Exchange(ctx context.Context, state, code string) (acces
id_token_nonce,
id_token_access_token_hash,
claims -- might want to open this up
id_token_custom_claims_family_name,
id_token_custom_claims_given_name,
id_token_custom_claims_name,
id_token_custom_claims_nickname,
id_token_custom_claims_picture,
id_token_custom_claims_updated_at
)
SELECT
@access_token,
@@ -187,7 +189,12 @@ func (a *Authenticator) Exchange(ctx context.Context, state, code string) (acces
@id_token_nonce,
@id_token_access_token_hash,
@claims
@id_token_custom_claims_family_name,
@id_token_custom_claims_given_name,
@id_token_custom_claims_name,
@id_token_custom_claims_nickname,
@id_token_custom_claims_picture,
@id_token_custom_claims_updated_at
FROM
the_user
`,
@@ -205,7 +212,12 @@ func (a *Authenticator) Exchange(ctx context.Context, state, code string) (acces
"id_token_nonce": idToken.Nonce,
"id_token_access_token_hash": idToken.AccessTokenHash,
"claims": json.RawMessage(claimsJSON),
"id_token_custom_claims_family_name": claims.FamilyName,
"id_token_custom_claims_given_name": claims.GivenName,
"id_token_custom_claims_name": claims.Name,
"id_token_custom_claims_nickname": claims.Nickname,
"id_token_custom_claims_picture": claims.Picture,
"id_token_custom_claims_updated_at": claims.UpdatedAt,
},
); err != nil {
return "", "", time.Time{}, fmt.Errorf("failed to perform query to save tokens: %w", err)
@@ -291,8 +303,6 @@ func (a *Authenticator) RefreshAccessToken(
return "", time.Time{}, err
}
claimsJSON, _ := json.Marshal(claims)
if _, err = a.db.Exec(
ctx,
`
@@ -318,7 +328,12 @@ func (a *Authenticator) RefreshAccessToken(
id_token_nonce,
id_token_access_token_hash,
claims -- might want to open this up
id_token_custom_claims_family_name,
id_token_custom_claims_given_name,
id_token_custom_claims_name,
id_token_custom_claims_nickname,
id_token_custom_claims_picture,
id_token_custom_claims_updated_at
)
VALUES (
@new_access_token,
@@ -334,7 +349,12 @@ func (a *Authenticator) RefreshAccessToken(
@id_token_nonce,
@id_token_access_token_hash,
@claims
@id_token_custom_claims_family_name,
@id_token_custom_claims_given_name,
@id_token_custom_claims_name,
@id_token_custom_claims_nickname,
@id_token_custom_claims_picture,
@id_token_custom_claims_updated_at
)
RETURNING
access_token AS new_access_token
@@ -363,7 +383,12 @@ func (a *Authenticator) RefreshAccessToken(
"id_token_nonce": idToken.Nonce,
"id_token_access_token_hash": idToken.AccessTokenHash,
"claims": json.RawMessage(claimsJSON),
"id_token_custom_claims_family_name": claims.FamilyName,
"id_token_custom_claims_given_name": claims.GivenName,
"id_token_custom_claims_name": claims.Name,
"id_token_custom_claims_nickname": claims.Nickname,
"id_token_custom_claims_picture": claims.Picture,
"id_token_custom_claims_updated_at": claims.UpdatedAt,
},
); err != nil {
return "", time.Time{}, fmt.Errorf("failed to save new access token and delete old access token: %w", err)
@@ -372,12 +397,14 @@ func (a *Authenticator) RefreshAccessToken(
return tkn.AccessToken, tkn.Expiry.UTC(), nil
}
func (a *Authenticator) verifyIDTokenAndClaimsFromToken(ctx context.Context, tkn *oauth2.Token) (idToken *oidc.IDToken, claims map[string]any, err error) {
func (a *Authenticator) verifyIDTokenAndClaimsFromToken(ctx context.Context, tkn *oauth2.Token) (idToken *oidc.IDToken, claims *AccessTokenClaims, err error) {
if idToken, err = a.VerifyIDToken(ctx, tkn); err != nil {
return nil, nil, fmt.Errorf("failed to verify id Token: %w", err)
}
if err := idToken.Claims(&claims); err != nil {
claims = new(AccessTokenClaims)
if err := idToken.Claims(claims); err != nil {
return nil, nil, fmt.Errorf("failed to obtain id token claims: %w", err)
}
+21 -7
View File
@@ -3,7 +3,6 @@ package authentication
import (
"context"
"crypto/rand"
"encoding/json"
"errors"
"fmt"
"ruben/inventory2/internal/consts"
@@ -102,7 +101,14 @@ func (a *Authenticator) GetAccessTokenClaimsAndExpiration(ctx context.Context, a
`
SELECT
expiry,
claims
id_token_custom_claims_name,
id_token_custom_claims_picture,
id_token_custom_claims_nickname,
id_token_custom_claims_given_name,
id_token_custom_claims_family_name,
id_token_custom_claims_updated_at
FROM
oauth_tokens
WHERE
@@ -117,8 +123,13 @@ func (a *Authenticator) GetAccessTokenClaimsAndExpiration(ctx context.Context, a
}
type Row struct {
Expiry time.Time
Claims json.RawMessage
Expiry time.Time
Id_token_custom_claims_name string
Id_token_custom_claims_picture string
Id_token_custom_claims_nickname string
Id_token_custom_claims_given_name string
Id_token_custom_claims_family_name string
Id_token_custom_claims_updated_at time.Time
}
r, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByNameLax[Row])
@@ -129,9 +140,12 @@ func (a *Authenticator) GetAccessTokenClaimsAndExpiration(ctx context.Context, a
return AccessTokenClaims{}, time.Time{}, fmt.Errorf("failed to scan row: %w", err)
}
if err := json.Unmarshal(r.Claims, &claims); err != nil {
return AccessTokenClaims{}, time.Time{}, fmt.Errorf("failed to scan claims json: %w", err)
}
claims.Name = r.Id_token_custom_claims_name
claims.Picture = r.Id_token_custom_claims_picture
claims.Nickname = r.Id_token_custom_claims_nickname
claims.GivenName = r.Id_token_custom_claims_given_name
claims.FamilyName = r.Id_token_custom_claims_family_name
claims.UpdatedAt = r.Id_token_custom_claims_updated_at
return claims, r.Expiry, nil
}
+30 -9
View File
@@ -11,12 +11,16 @@ import (
"github.com/gin-gonic/gin"
)
const (
maxNumOpenConnectionsPerUser = 3
)
type (
sseRouter struct {
log *logging.Logger
sse *sse.Queue
users map[string]context.CancelFunc
users map[string][maxNumOpenConnectionsPerUser]context.CancelFunc
lock sync.Mutex
}
)
@@ -30,7 +34,7 @@ func Routes(
s := &sseRouter{
log: logger,
sse: sq,
users: make(map[string]context.CancelFunc),
users: make(map[string][maxNumOpenConnectionsPerUser]context.CancelFunc),
}
r.GET("/", auth.AuthenticateAndAddIdentityGin(), response.Handler(s.serveEvents))
@@ -46,10 +50,11 @@ func (r *sseRouter) serveEvents(c *gin.Context) (response.Response, error) {
"accountID", acctID,
"userID", userID,
"email", email,
"userAgent", c.Request.UserAgent(),
)
log.Info("user connected to sse queue")
ctx := r.closeExistingConnectionsForUserAndStoreCancelFuncForUser(c, userID)
ctx := r.closeOutstandingConnectionsForUserAndStoreCancelFuncForUser(c, userID)
w := c.Writer
@@ -80,18 +85,34 @@ func (r *sseRouter) serveEvents(c *gin.Context) (response.Response, error) {
return response.Status(200), nil
}
func (r *sseRouter) closeExistingConnectionsForUserAndStoreCancelFuncForUser(ctx context.Context, userID string) context.Context {
func (r *sseRouter) closeOutstandingConnectionsForUserAndStoreCancelFuncForUser(ctx context.Context, userID string) context.Context {
r.lock.Lock()
defer r.lock.Unlock()
ctx, cancel := context.WithCancel(ctx)
closeConnFuncs := r.users[userID]
defer func() {
r.users[userID] = closeConnFuncs
}()
// close existing connection
if closeConn, ok := r.users[userID]; ok {
closeConn()
delete(r.users, userID)
for i := range maxNumOpenConnectionsPerUser {
if fn := closeConnFuncs[i]; fn == nil {
// not hit limit on connections.
// save the cancellation func and done
closeConnFuncs[i] = cancel
return ctx
}
}
// store reference to cancel func
ctx, r.users[userID] = context.WithCancel(ctx)
// close the oldest connection, shift all cancellation funcs down, and push the new one in
closeConnFuncs[0]()
for i := range maxNumOpenConnectionsPerUser - 1 {
closeConnFuncs[i] = closeConnFuncs[i+1]
}
closeConnFuncs[maxNumOpenConnectionsPerUser-1] = cancel
return ctx
}