Skip to content

feat: support for oidc max age#949

Open
steveiliop56 wants to merge 3 commits into
mainfrom
feat/oidc-max-age
Open

feat: support for oidc max age#949
steveiliop56 wants to merge 3 commits into
mainfrom
feat/oidc-max-age

Conversation

@steveiliop56

@steveiliop56 steveiliop56 commented Jun 19, 2026

Copy link
Copy Markdown
Member

Depends on #948

Summary by CodeRabbit

  • New Features
    • Added support for the OIDC prompt parameter (none/login) to control authentication flow, including silent auth when requested.
    • Enabled automatic authorization on the authorize page when silent authentication conditions are met, and adjusted button/redirect behavior accordingly.
    • Introduced authentication timestamp tracking and propagation into OIDC token generation, with refresh regenerating ID tokens using a reset auth-time value.

@dosubot dosubot Bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Jun 19, 2026
@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 054e8971-a464-4b20-a16a-3154c7942266

📥 Commits

Reviewing files that changed from the base of the PR and between 97eadbc and 95fc108.

📒 Files selected for processing (1)
  • internal/controller/oidc_controller.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/controller/oidc_controller.go

📝 Walkthrough

Walkthrough

Adds OIDC prompt (none/login) and max_age parameter enforcement to the full stack. The backend gains a new OIDCPrompt type, prompt validation in the authorize controller, auth_time propagation through UserContext, AuthorizeCodeEntry, and ID token claims. The frontend's ScreenParams, AuthorizePage, and LoginPage are updated to consume and round-trip oidc_prompt.

Changes

OIDC prompt enforcement and auth_time propagation

Layer / File(s) Summary
OIDC service types: OIDCPrompt, auth_time, AuthorizeRequest fields
internal/service/oidc_service.go
Defines OIDCPrompt type with login/none constants and SupportedPrompts; adds Prompt/MaxAge to AuthorizeRequest; adds AuthTime to ClaimSet, AuthorizeCodeEntry; extends generateIDToken and GenerateAccessToken to accept authTime; implements GetPrompt parser; stores auth_time in CreateCode; passes 0 for auth_time during refresh.
UserContext AuthTime field and session wiring
internal/model/context.go
Adds exported AuthTime int64 to UserContext and populates it from session.CreatedAt in NewFromSession.
OIDC controller: prompt enforcement, auth_time wiring, authorizeComplete fix
internal/controller/oidc_controller.go
Imports strconv for max_age parsing; extends AuthorizeScreenParams with OIDCPrompt; reworks the authorize endpoint to validate prompts, reject unauthenticated requests for prompt=none, parse max_age, and include OIDCPrompt in redirect params. Fixes authorizeComplete to treat ErrUserContextNotFound as unauthenticated. Updates Token endpoint to pass entry.AuthTime into GenerateAccessToken.
Frontend screen-params: oidc_prompt type and schema
frontend/src/lib/hooks/screen-params.ts
Adds optional oidc_prompt to ScreenParams type and the corresponding Zod schema enum.
Frontend authorize and login page: prompt-driven behavior
frontend/src/pages/authorize-page.tsx, frontend/src/pages/login-page.tsx
AuthorizePage computes shouldAutoAuthorize for prompt=none, fires the authorize mutation via useEffect, forces login redirect for oidc_prompt=login, and disables buttons during auto-authorization. LoginPage strips oidc_prompt from the compiled OAuth URL and skips authenticated-user redirect when oidc_prompt=login.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • tinyauthapp/tinyauth#923: Introduced the AuthorizeRequest JWT decoding and ticket flow that this PR directly extends with Prompt/MaxAge fields and auth_time propagation.
  • tinyauthapp/tinyauth#630: Modifies the same frontend/src/pages/login-page.tsx OIDC control flow paths and OAuth URL compilation that this PR further extends with oidc_prompt behavior.

Suggested reviewers

  • Rycochet
  • scottmckendry

Poem

🐇 A prompt of "none" came hopping along,
auth_time now echoes in every ID song!
The login page guards with a careful eye,
while auto-authorize lets the token fly.
Tinyauth grows, one claim at a time —
🎉 this bunny thinks the OIDC flow is prime!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title mentions max_age support, but the changeset primarily implements OIDC prompt parameter support ('none'/'login') with max_age as a secondary consideration. The main functional changes focus on prompt-based authorization flow control. Consider revising the title to reflect the primary change, such as 'feat: support for oidc prompt parameter' or 'feat: implement oidc prompt and max age support' to better represent the full scope of changes.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/oidc-max-age

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov

codecov Bot commented Jun 19, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 10.66667% with 67 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/controller/oidc_controller.go 12.28% 43 Missing and 7 partials ⚠️
internal/service/oidc_service.go 0.00% 17 Missing ⚠️

📢 Thoughts on this report? Let us know!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/service/oidc_service.go (1)

938-949: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

max_age is not extracted from JWT claims.

DecodeAuthorizeJWT extracts prompt but not max_age. If a client sends the authorize request as a JWT (request object), the max_age claim will be ignored while req.MaxAge remains empty.

🔧 Proposed fix
 	return &AuthorizeRequest{
 		Scope:               get("scope"),
 		ResponseType:        get("response_type"),
 		ClientID:            get("client_id"),
 		RedirectURI:         get("redirect_uri"),
 		State:               get("state"),
 		Nonce:               get("nonce"),
 		CodeChallenge:       get("code_challenge"),
 		CodeChallengeMethod: get("code_challenge_method"),
 		Prompt:              get("prompt"),
+		MaxAge:              get("max_age"),
 	}, nil
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/service/oidc_service.go` around lines 938 - 949, The
DecodeAuthorizeJWT function is missing extraction of the max_age claim from the
JWT when constructing the AuthorizeRequest struct. Add a new field MaxAge to the
AuthorizeRequest return statement and populate it using the get() method with
the parameter "max_age", following the same pattern used for other JWT claims
like prompt, state, and nonce.
🧹 Nitpick comments (1)
internal/service/oidc_service.go (1)

676-678: ⚡ Quick win

Consider OIDC spec compliance for auth_time during refresh.

Setting auth_time to 0 emits "auth_time": 0 in the ID token (Unix epoch). Per OIDC Core spec, auth_time should reflect the original authentication time. Consider either:

  1. Persisting auth_time in the OIDC session DB and retrieving it during refresh
  2. Omitting auth_time entirely from refreshed tokens (use omitempty behavior)

The current approach could confuse relying parties that validate auth_time.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/service/oidc_service.go` around lines 676 - 678, The generateIDToken
call in the refresh token flow is passing a hardcoded auth_time value of 0,
which violates OIDC spec compliance. Either persist the original auth_time value
when the OIDC session is first created and stored in the entry object, then
retrieve and pass that value during refresh instead of 0, OR modify the
generateIDToken function and the underlying token generation logic to support
omitting auth_time entirely from the JWT when it's not available (using
omitempty struct tags). This ensures that refreshed ID tokens either contain the
original authentication time or exclude the field entirely rather than emitting
a zero Unix timestamp.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/controller/oidc_controller.go`:
- Around line 222-242: The code accesses userContext.Authenticated and
userContext.AuthTime in the max_age validation block without checking if
userContext is nil or in a valid state. If NewFromGin returns an error other
than ErrUserContextNotFound, userContext could be nil or partially initialized,
leading to a nil dereference. Add a nil check for userContext before the
condition that checks userContext.Authenticated to ensure the userContext is
properly initialized before accessing its fields.

---

Outside diff comments:
In `@internal/service/oidc_service.go`:
- Around line 938-949: The DecodeAuthorizeJWT function is missing extraction of
the max_age claim from the JWT when constructing the AuthorizeRequest struct.
Add a new field MaxAge to the AuthorizeRequest return statement and populate it
using the get() method with the parameter "max_age", following the same pattern
used for other JWT claims like prompt, state, and nonce.

---

Nitpick comments:
In `@internal/service/oidc_service.go`:
- Around line 676-678: The generateIDToken call in the refresh token flow is
passing a hardcoded auth_time value of 0, which violates OIDC spec compliance.
Either persist the original auth_time value when the OIDC session is first
created and stored in the entry object, then retrieve and pass that value during
refresh instead of 0, OR modify the generateIDToken function and the underlying
token generation logic to support omitting auth_time entirely from the JWT when
it's not available (using omitempty struct tags). This ensures that refreshed ID
tokens either contain the original authentication time or exclude the field
entirely rather than emitting a zero Unix timestamp.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 30182146-d821-4934-bb60-ff56143d5be2

📥 Commits

Reviewing files that changed from the base of the PR and between 6ccc894 and 97eadbc.

📒 Files selected for processing (6)
  • frontend/src/lib/hooks/screen-params.ts
  • frontend/src/pages/authorize-page.tsx
  • frontend/src/pages/login-page.tsx
  • internal/controller/oidc_controller.go
  • internal/model/context.go
  • internal/service/oidc_service.go

Comment thread internal/controller/oidc_controller.go Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant