mirror of
https://tangled.org/evan.jarrett.net/at-container-registry
synced 2026-04-20 16:40:29 +00:00
69 lines
2.0 KiB
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
|
|
}
|