Files
at-container-registry/pkg/appview/handlers/oauth_errors.go

69 lines
2.0 KiB
Go

package handlers
import (
"context"
"errors"
"log/slog"
"strings"
"atcr.io/pkg/auth/oauth"
"github.com/bluesky-social/indigo/atproto/atclient"
"github.com/bluesky-social/indigo/xrpc"
)
// isOAuthError checks if an error indicates OAuth authentication failure
// These errors indicate the OAuth session is invalid and should be cleaned up
// Uses structured error types to avoid false positives from substring matching
func isOAuthError(err error) bool {
if err == nil {
return false
}
// Check structured error types first
var xrpcErr *xrpc.Error
if errors.As(err, &xrpcErr) && (xrpcErr.StatusCode == 401 || xrpcErr.StatusCode == 403) {
return true
}
var apiErr *atclient.APIError
if errors.As(err, &apiErr) {
if apiErr.StatusCode == 401 || apiErr.StatusCode == 403 {
return true
}
if apiErr.Name == "InvalidToken" || apiErr.Name == "InsufficientScope" || apiErr.Name == "InvalidGrant" {
return true
}
}
// Fallback: check for known auth-specific error strings that won't
// appear in digests or URIs
errStr := strings.ToLower(err.Error())
return strings.Contains(errStr, "invalid_token") ||
strings.Contains(errStr, "invalid_grant") ||
strings.Contains(errStr, "use_dpop_nonce") ||
strings.Contains(errStr, "authentication failed") ||
strings.Contains(errStr, "token expired")
}
// handleOAuthError checks if an error is OAuth-related and invalidates UI sessions if so
// Returns true if the error was an OAuth error (caller should return early)
func handleOAuthError(ctx context.Context, refresher *oauth.Refresher, did string, err error) bool {
if !isOAuthError(err) {
return false
}
slog.Warn("OAuth error detected, invalidating sessions",
"component", "handlers",
"did", did,
"error", err)
// Invalidate all UI sessions for this DID
if delErr := refresher.DeleteSession(ctx, did); delErr != nil {
slog.Warn("Failed to delete OAuth session after error",
"component", "handlers",
"did", did,
"error", delErr)
}
return true
}