fix scope mismatch?

This commit is contained in:
Evan Jarrett
2025-12-26 17:41:38 -06:00
parent 012a14c4ee
commit c1f2ae0f7a
2 changed files with 31 additions and 30 deletions
-6
View File
@@ -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
View File
@@ -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