mirror of
https://tangled.org/evan.jarrett.net/at-container-registry
synced 2026-09-10 04:06:06 +00:00
fix scope mismatch?
This commit is contained in:
@@ -14,12 +14,6 @@
|
||||
"resource": "repo",
|
||||
"action": ["create", "update", "delete"],
|
||||
"collection": ["io.atcr.manifest", "io.atcr.tag", "io.atcr.sailor.star", "io.atcr.sailor.profile", "io.atcr.repo.page"]
|
||||
},
|
||||
{
|
||||
"type": "permission",
|
||||
"resource": "rpc",
|
||||
"lxm": ["com.atproto.repo.getRecord"],
|
||||
"aud": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
+31
-24
@@ -82,13 +82,8 @@ func GetDefaultScopes(did string) []string {
|
||||
// See lexicons/io/atcr/authFullApp.json for definition
|
||||
// Uses "include:" prefix per ATProto permission spec
|
||||
"include:io.atcr.authFullApp",
|
||||
// Individual repo/rpc scopes (for current PDS compatibility)
|
||||
// fmt.Sprintf("repo:%s", atproto.ManifestCollection),
|
||||
// fmt.Sprintf("repo:%s", atproto.TagCollection),
|
||||
// fmt.Sprintf("repo:%s", atproto.StarCollection),
|
||||
// fmt.Sprintf("repo:%s", atproto.SailorProfileCollection),
|
||||
// fmt.Sprintf("repo:%s", atproto.RepoPageCollection),
|
||||
// "rpc:com.atproto.repo.getRecord?aud=*",
|
||||
// com.atproto scopes must be separate (permission-sets are namespace-limited)
|
||||
"rpc:com.atproto.repo.getRecord?aud=*",
|
||||
// Blob scopes (not supported in Lexicon permission-sets)
|
||||
// Image manifest types (single-arch)
|
||||
"blob:application/vnd.oci.image.manifest.v1+json",
|
||||
@@ -228,6 +223,18 @@ func (r *Refresher) DoWithSession(ctx context.Context, did string, fn func(sessi
|
||||
// The session's PersistSessionCallback will save nonce updates to DB
|
||||
err = fn(session)
|
||||
|
||||
// If request failed with auth error, delete session to force re-auth
|
||||
if err != nil && isAuthError(err) {
|
||||
slog.Warn("Auth error detected, deleting session to force re-auth",
|
||||
"component", "oauth/refresher",
|
||||
"did", did,
|
||||
"error", err)
|
||||
// Don't hold the lock while deleting - release first
|
||||
mutex.Unlock()
|
||||
_ = r.DeleteSession(ctx, did)
|
||||
mutex.Lock() // Re-acquire for the deferred unlock
|
||||
}
|
||||
|
||||
slog.Debug("Released session lock for DoWithSession",
|
||||
"component", "oauth/refresher",
|
||||
"did", did,
|
||||
@@ -236,6 +243,19 @@ func (r *Refresher) DoWithSession(ctx context.Context, did string, fn func(sessi
|
||||
return err
|
||||
}
|
||||
|
||||
// isAuthError checks if an error looks like an OAuth/auth failure
|
||||
func isAuthError(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
errStr := strings.ToLower(err.Error())
|
||||
return strings.Contains(errStr, "unauthorized") ||
|
||||
strings.Contains(errStr, "invalid_token") ||
|
||||
strings.Contains(errStr, "insufficient_scope") ||
|
||||
strings.Contains(errStr, "token expired") ||
|
||||
strings.Contains(errStr, "401")
|
||||
}
|
||||
|
||||
// resumeSession loads a session from storage
|
||||
func (r *Refresher) resumeSession(ctx context.Context, did string) (*oauth.ClientSession, error) {
|
||||
// Parse DID
|
||||
@@ -260,28 +280,15 @@ func (r *Refresher) resumeSession(ctx context.Context, did string) (*oauth.Clien
|
||||
return nil, fmt.Errorf("no session found for DID: %s", did)
|
||||
}
|
||||
|
||||
// Validate that session scopes match current desired scopes
|
||||
// Log scope differences for debugging, but don't delete session
|
||||
// The PDS will reject requests if scopes are insufficient
|
||||
// (Permission-sets get expanded by PDS, so exact matching doesn't work)
|
||||
desiredScopes := r.clientApp.Config.Scopes
|
||||
if !ScopesMatch(sessionData.Scopes, desiredScopes) {
|
||||
slog.Debug("Scope mismatch, deleting session",
|
||||
slog.Debug("Session scopes differ from desired (may be permission-set expansion)",
|
||||
"did", did,
|
||||
"storedScopes", sessionData.Scopes,
|
||||
"desiredScopes", desiredScopes)
|
||||
|
||||
// Delete the session from database since scopes have changed
|
||||
if err := r.clientApp.Store.DeleteSession(ctx, accountDID, sessionID); err != nil {
|
||||
slog.Warn("Failed to delete session with mismatched scopes", "error", err, "did", did)
|
||||
}
|
||||
|
||||
// Also invalidate UI sessions since OAuth is now invalid
|
||||
if r.uiSessionStore != nil {
|
||||
r.uiSessionStore.DeleteByDID(did)
|
||||
slog.Info("Invalidated UI sessions due to scope mismatch",
|
||||
"component", "oauth/refresher",
|
||||
"did", did)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("OAuth scopes changed, re-authentication required")
|
||||
}
|
||||
|
||||
// Resume session
|
||||
|
||||
Reference in New Issue
Block a user