Skip to content

feat(user): add User#resource_token for delegated SSO access - #333

Merged
stakach merged 1 commit into
masterfrom
feat/user-resource-token
Oct 8, 2026
Merged

stakach merged 1 commit into
masterfrom
feat/user-resource-token

Conversation

@stakach

@stakach stakach commented Oct 7, 2026

Copy link
Copy Markdown
Member

Summary

  • Adds User#resource_token(authority = user.authority) : User::ResourceToken. This is rest-api's Api::Users.get_user_token moved into models, so staff-api can get a user's delegated SSO token (MS Graph / Google) without calling rest-api.
  • Adds Model::Error::NoResourceToken, raised when there's no refresh token or no OAuth strategy. rest-api currently returns a 404 in that case.
  • Behaviour is unchanged otherwise:
    • The stored token is returned if it doesn't expire or has more than 5 minutes left.
    • Otherwise it is refreshed against the authority's oauth-strategy (or the authority's first strategy) and saved.
    • If the refresh fails, a token that hasn't expired yet (15s clock-drift allowance) is still returned.
  • Small fix: a relative token_url (e.g. the default /oauth/token) is now resolved against the strategy's site. Previously it crashed on hostname.not_nil!. The token endpoint's scheme and port are now respected too.

Part of removing staff-api's intra-service calls to rest-api. Follow-ups: staff-api and rest-api will use this method.

Test plan

  • ./test spec/user_resource_token_spec.cr spec/user_spec.cr: 44 examples, 0 failures. Covers non-expiring / fresh / near-expiry refresh, refresh-token rotation, configured strategy, fallback within the drift window, raise when expired, no refresh token, and no strategy. Uses a local HTTP token server.

🤖 Generated with Claude Code

Moves the SSO resource token logic out of rest-api so other services
(staff-api) can obtain a user's delegated token directly instead of making
an intra-service request.

The stored access token is returned while it has more than five minutes
remaining, otherwise it is refreshed against the authority's OAuth strategy
and saved. A token that hasn't expired yet is still returned if the refresh
fails. Raises `Error::NoResourceToken` when there is nothing to refresh with.

Relative `token_url`s are now resolved against the strategy's `site`.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added the type: enhancement new feature or request label Oct 7, 2026
@stakach
stakach merged commit f775bd1 into master Oct 8, 2026
8 checks passed
@stakach
stakach deleted the feat/user-resource-token branch October 8, 2026 00:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: enhancement new feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant