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 RefreshRequest

View on GitHub (pinned to 7bedaf55a0)

Solutions

  1. Inspect the wrapped error (expired vs signature vs malformed)
  2. Have the client fall back to a full Signin when the refresh token is expired
  3. Ensure the client sends the refresh token (not the access token) to RefreshHandler
  4. 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

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.