kataras/iris · error
auth: refresh: %w
Error message
auth: refresh: %w
What it means
Auth.Refresh failed while verifying the supplied refresh token via the internal s.verify(). The refresh token could not be parsed, its signature is invalid, or it has expired.
Source
Thrown at auth/auth.go:481
if accessToken == "" {
if cookieName := s.config.Cookie.Name; cookieName != "" {
accessToken = ctx.GetCookie(cookieName, context.CookieEncoding(s.securecookie))
}
}
return accessToken
}
// Refresh accepts a previously generated refresh token (from SigninHandler) and
// returns a new access and refresh token pair.
func (s *Auth[T]) Refresh(ctx stdContext.Context, refreshToken []byte) ([]byte, []byte, error) {
if !s.refreshEnabled {
return nil, nil, fmt.Errorf("auth: refresh: disabled")
}
t, _, err := s.verify(ctx, refreshToken)
if err != nil {
return nil, nil, fmt.Errorf("auth: refresh: %w", err)
}
// refresh the tokens, both refresh & access tokens will be renew to prevent
// malicious 😈 users that may hold a refresh token.
accessTok, refreshTok, err := s.sign(t)
if err != nil {
return nil, nil, fmt.Errorf("auth: refresh: %w", err)
}
return accessTok, refreshTok, nil
}
// RefreshHandler reads the request body which should include data for `RefreshRequest` structure
// and sends a new access and refresh token pair,
// also sets the cookie to the new encrypted access token value.
// See `Refresh` method for more.
func (s *Auth[T]) RefreshHandler(ctx *context.Context) {
var req RefreshRequestView on GitHub (pinned to 7bedaf55a0)
Solutions
- Inspect the wrapped error (expired vs signature vs malformed)
- Have the client fall back to a full Signin when the refresh token is expired
- Ensure the client sends the refresh token (not the access token) to RefreshHandler
- Keep KIDRefresh key material stable across instances, or persist old keys for a grace window
Example fix
// before
newAccess, newRefresh, err := auth.Refresh(ctx, body.Token) // sends access token
// after
newAccess, newRefresh, err := auth.Refresh(ctx, body.RefreshToken)
if err != nil { // expired: force re-login
http.Error(w, "session expired", 401); return
} Defensive patterns
Strategy: try-catch
Validate before calling
body := new(RefreshRequest)
if err := json.NewDecoder(r.Body).Decode(body); err != nil || len(body.RefreshToken) == 0 {
http.Error(w, "refresh token required", http.StatusBadRequest); return
} Type guard
func isRefreshToken(b []byte) bool { return len(b) > 0 && strings.Count(string(b), ".") == 2 } Try / catch
access, refresh, err := auth.Refresh(ctx, refreshToken)
if err != nil {
// expired/invalid refresh token cannot be renewed — force full re-login
http.Error(w, "session expired, please sign in", http.StatusUnauthorized); return
} Prevention
- Always send the refresh token, not the access token, to the refresh endpoint
- Treat any Refresh verification failure as terminal: require Signin, never retry the same token
- Keep KIDRefresh key material stable or support a verification grace window during rotation
- Set refresh-token TTL appropriately for expected session lengths
When it happens
Trigger: Auth.Refresh(ctx, refreshToken) receives token bytes that fail s.verify: expired refresh token, signed with a revoked/rotated KIDRefresh key, or garbage/truncated bytes from the client.
Common situations: A client holds a refresh token past its TTL; the server rotated its refresh signing secret; the client sent an access token instead of a refresh token to the refresh endpoint.
Related errors
AI-assisted analysis of kataras/iris@7bedaf55a0 (2026-08-30).
Data as JSON: /api/errors/ae52ec62503aaf8b.
Report an issue: GitHub.