From bdc1f5f0e950ea9807c91c8e5137c90a8fdb62f4 Mon Sep 17 00:00:00 2001 From: bryan newbold Date: Mon, 13 Apr 2026 19:38:44 -0700 Subject: [PATCH] auth: iterate on purge codepath error handling --- atproto/auth/jwt.go | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/atproto/auth/jwt.go b/atproto/auth/jwt.go index 8ca75c99..0cca07a2 100644 --- a/atproto/auth/jwt.go +++ b/atproto/auth/jwt.go @@ -47,42 +47,44 @@ func (s *ServiceAuthValidator) Validate(ctx context.Context, tokenString string, } token, err := jwt.ParseWithClaims(tokenString, &serviceAuthClaims{}, s.fetchIssuerKeyFunc(ctx), opts...) + + // if signature validation fails, purge the directory and try again + // TODO: probably need to cache or rate-limit this? if err != nil && errors.Is(err, jwt.ErrTokenSignatureInvalid) { - // if signature validation fails, purge the directory and try again - // TODO: probably need to cache or rate-limit this? // do an unvalidated extraction of 'iss' from JWT insecure := jwt.NewParser(jwt.WithoutClaimsValidation()) - t, _, err := insecure.ParseUnverified(tokenString, &jwt.MapClaims{}) - if err != nil { - return "", fmt.Errorf("parse unverified %q: %w", tokenString, err) + t, _, purgeErr := insecure.ParseUnverified(tokenString, &jwt.MapClaims{}) + if purgeErr != nil { + return "", purgeErr } claims, ok := t.Claims.(*jwt.MapClaims) if !ok { return "", jwt.ErrTokenInvalidClaims } - iss, err := claims.GetIssuer() - if err != nil { - return "", err + iss, purgeErr := claims.GetIssuer() + if purgeErr != nil { + return "", purgeErr } - did, err := syntax.ParseDID(iss) - if err != nil { - return "", fmt.Errorf("%w: invalid DID: %w", jwt.ErrTokenInvalidIssuer, err) + did, purgeErr := syntax.ParseDID(iss) + if purgeErr != nil { + return "", fmt.Errorf("%w: invalid DID: %w", jwt.ErrTokenInvalidIssuer, purgeErr) } slog.Info("purging directory and retrying service auth signature validation", "did", did) - err = s.Dir.Purge(ctx, did.AtIdentifier()) - if err != nil { - slog.Error("purging identity directory", "did", did, "err", err) + purgeErr = s.Dir.Purge(ctx, did.AtIdentifier()) + if purgeErr != nil { + slog.Error("purging identity directory", "did", did, "err", purgeErr) } token, err = jwt.ParseWithClaims(tokenString, &serviceAuthClaims{}, s.fetchIssuerKeyFunc(ctx), opts...) - if err != nil { - return "", fmt.Errorf("parse with claims %q: %w", tokenString, err) - } + // 'err' checked again just below } + + // all other errors (or second signature error) if err != nil { return "", err } + claims, ok := token.Claims.(*serviceAuthClaims) if !ok { // TODO: is the error message returned descriptive enough? -- 2.51.2