Suprnova exposes OAuth, Sign in with Apple, and passwordless magic links through
the framework-owned Auth facade. Magnetar supplies the credential, ceremony,
identity, factor-gate, and session engines behind that facade.
The public entry points are:
Auth::oauth(provider)for OAuth and Apple.Auth::magic_link()for passwordless email login.
Suprnova does not install routes for these flows. Applications provide small start and callback handlers and decide how to deliver magic-link email.
Initialize Magnetar with OAuth
Configure OAuth on the same MagnetarConfig that initializes password,
passkey, session, lockout, and two-factor services. The provider registry is
published atomically with those services: if any service cannot be built, none
of them becomes visible.
use Arc;
use ;
pub async
The framework re-exports the OAuthProvider contract, the five first-party
providers and configuration types, and every type needed to implement a custom
provider. ReqwestOAuthTransport supplies production token, userinfo, and
revocation I/O. FrameworkAbuseLimiter uses the application's configured
RateLimiterDriver. Applications need neither a direct suprnova-magnetar
dependency nor hand-written transport and limiter adapters.
MagnetarConfig creates its schema when apply_migrations is enabled, which is
the default. Use .apply_migrations(false) only when deployment prepares the
same schema separately. A second initialization returns an error instead of
replacing any installed engine.
Keep an existing user and session stack
An application can use Magnetar for OAuth ceremonies and provider proof without
making Magnetar authoritative for password, passkey, framework-session, or
remember-me state. Build the same MagnetarOAuthHostConfig, then install it
through the OAuth-only initializer:
use ;
let database = DBconnection?;
init_magnetar_oauth_only
.await?;
Start the ceremony normally with Auth::oauth(provider).begin(). In the
callback, call verify_oauth_identity(code, state), map the verified provider
subject into the application's own user table, and establish the existing
framework session with Auth::login. Do not call complete in this mode:
complete applies Magnetar's default account and session mapping, while the
purpose of OAuth-only initialization is to leave those decisions with the
application.
OAuth-only and full default initialization are alternatives. A second initializer fails instead of mixing session authorities.
GitHub provider requirements
GitHub's REST user endpoint requires a User-Agent; a community provider adds
it, and any media-type Accept value it needs, through
OAuthProvider::userinfo_headers. Suprnova adds the bearer Authorization
header separately and rejects provider attempts to override it.
GitHub's /user response includes an email only when the user made it public.
The verified primary address requires a second /user/emails request, while
resolve_identity deliberately performs no I/O and receives one userinfo
response. A GitHub provider can return email: None and use Suprnova's email
completion ceremony, or point userinfo_endpoint at a host adapter that
combines /user with the verified primary email. Do not treat an unverified or
merely public address as account ownership.
Report the account picture
Each first-party provider reads the account picture from the profile response it already parses:
- Google reads the userinfo
pictureclaim. - TikTok reads
avatar_url. - Facebook reads
picture.data.url. Its profile request namesfields=id,name,email,picture, because the Graph API returns onlyidandnamefor a request that names no fields. - X reads
profile_image_url, which its profile request names inuser.fields. - Apple reports no picture.
A custom provider reports a picture by adding OAuthProvider::avatar_url. The
method receives the same ProviderResponse that resolve_identity parses, and
it performs no I/O. Its default returns None, so a provider written without
it compiles unchanged and reports no picture.
use ;
Return None for a missing or empty picture rather than an error: a profile
without a picture still signs in. Keep the picture out of the struct
resolve_identity deserializes, as the example does: there, a picture of a
shape you didn't expect fails the whole profile, and the sign-in with it. If
the provider's API omits fields that the request doesn't name, add the
picture field to userinfo_endpoint.
Session binding
OAuth begin requires SessionMiddleware. Magnetar binds the ceremony to a
digest of the initiating framework session, so the callback cannot be moved to
another browser session.
Successful password, magic-link, passkey, and OAuth sign-in rotates the framework session ID and CSRF token, records the application user ID, and stores an opaque Magnetar web binding. Remember-me hydration rotates both the Magnetar credential and the framework session binding.
Start an OAuth flow
Use begin in the provider's start handler:
use Auth;
let kickoff = oauth.begin.await?;
// Return an HTTP redirect to kickoff.authorization_url.
The returned OAuthKickoff contains:
authorization_url, the URL to send to the browser.state, the single-use selector bound to the initiating session.
begin marks that session for storage, so the start response always sets
the session cookie. The start can be the browser's first request to your
application, and it can be a JSON or POST request; the callback still
arrives under the session the ceremony is bound to.
Magnetar owns state generation, PKCE policy, ceremony persistence, provider exchange, identity verification, and abuse limiting. The host controller owns the HTTP redirect and callback route.
Verify or complete the callback
The callback has two entry points:
| Method | Result | Side effects |
|---|---|---|
verify_oauth_identity(code, state) |
OAuthIdentity |
Verifies the provider proof and returns the provider, subject, verified email, display name, and picture URL without creating an application session. |
complete(code, state) |
(User, Session) |
Resolves the identity through the installed host engine, applies account-link policy and the factor gate, rotates the framework session, and returns the framework-owned user and Magnetar session values. |
let identity = oauth
.verify_oauth_identity
.await?;
let = oauth
.complete
.await?;
OAuthIdentity.email is present only when the provider supplied a verified
email. Persist the provider and subject as the stable external identity. Email
is not a stable provider identifier.
OAuthIdentity.avatar_url holds the account picture URL when the provider
reports one. It's None when the profile has no picture, an empty one, or one
that isn't the string the provider documents, and the picture never fails the
callback. The owner of the provider account
controls this value, so treat it as untrusted profile data. Before you render,
fetch, or store it, check its scheme and length:
let avatar_url = identity
.avatar_url
.filter;
Account-link policy
OAuth completion does not treat possession of an unverified email string as proof that the caller owns an existing application account.
The completion result can require more work instead of issuing a session:
- Email completion required returns HTTP 409 when the provider identity needs a separate verified-email ceremony.
- Explicit link required returns HTTP 409 when an existing verified account must authorize the link.
- Factor required returns HTTP 401 when account policy requires a second factor before session issuance.
A verified-email completion that wins the first-email-proof boundary reclaims an unverified squatted account atomically. The transaction advances the auth epoch, removes provisional credentials, revokes old sessions and remember credentials, and attaches the verified provider account. A verified account is never auto-linked by email alone.
Sign in with Apple
Apple uses the same Auth::oauth("apple") facade, but its callback commonly
uses response_mode=form_post. Register the callback as a POST route and
pass the optional Apple user form field through the Apple-specific methods:
let identity = oauth
.verify_apple_identity
.await?;
let = oauth
.complete_with_apple_form_post
.await?;
AppleIdentity includes the stable subject, optional verified email,
email_verified, and is_private_email. Persist the subject as the stable key.
Apple can supply the display name only during the first authorization, so the
provider adapter must preserve that first form_post value.
Apple token and identity verification belongs to the installed provider implementation. Current Magnetar providers require signature, issuer, audience, expiry, and nonce checks rather than trusting an ID token's decoded JSON.
Magic-link login
Magic-link login uses the installed Magnetar password/session engine. The framework returns the plaintext single-use token, while the application owns mail composition and URL shape:
use ;
let token = magic_link
.send
.await?;
let url = format!;
to
.send
.await?;
let = magic_link.consume.await?;
send applies the authentication abuse budget before token issuance. consume
is single-use, applies the factor gate, binds the resulting session into the
framework request session, and returns the user and Magnetar session.
For an unverified pre-existing account, successful magic-link consumption is a first email proof. The transaction reclaims the account and removes provisional password, passkey, linked-account, two-factor, session, and remember state so a prior squatter cannot retain access.
Routes to add
A typical application adds these routes:
get!,
get!,
post!,
post!,
get!,
Apply SessionMiddleware to every OAuth and passkey start/callback route. The
session carries the ceremony selector and binds the round trip to the browser
that started it.
Authentication migration
The suprnova-magnetar crate includes a shape-aware migration engine for
Torii, Suprnova web, Suprnova API, and existing Magnetar schemas. It is a
library surface and example, not a suprnova CLI subcommand.
Enable the migration feature plus the source database driver and run a dry
plan before applying. For PostgreSQL:
cargo run -p suprnova-magnetar \
--features migration,seaorm-postgres \
--example migrate -- \
--source-shape torii \
--database-url "$SOURCE_DATABASE_URL" \
--app-database-url "$DATABASE_URL"
Use seaorm-mysql or seaorm-sqlite instead when that is the source and
application database driver.
Add --apply to apply the reviewed plan. The runner rechecks source and schema
fingerprints before import, records retry state, refuses identity collisions,
and uses transactional imports. MySQL same-database migrations use a
write-barrier-protected shadow swap with resumable restore and abort paths.
Keep the generated plan and report in deployment records. Do not apply a plan whose source fingerprint changed after review.
Reference
- Default boot:
MagnetarConfig,PasskeyConfig, andinit_magnetar. - Facades:
Auth::oauth(provider)andAuth::magic_link(). - OAuth installation:
MagnetarConfig::oauth,ReqwestOAuthTransport, andFrameworkAbuseLimiter. - Migration library:
magnetar::migrationfrom thesuprnova-magnetarcrate. - Bearer authentication:
BearerTokenMiddleware.
Next
- Authentication covers password, passkey, guards, framework sessions, and engine initialization.
- Auth flows covers email verification, password reset, lockout, and two-factor authentication.
- Mail covers application-owned magic-link delivery.
- Session covers the browser session that binds OAuth and passkey ceremonies.
