AUTH4D-269 verified email step on the hosted device approval page
Native games sign in through device authorization (auth4d-16.md (repository file docs/api-spec/auth4d-16.md)). When the player
signs in on the hosted device page with Steam (or another social method) and the application’s
requireVerifiedEmail (auth4d-260.md (repository file docs/api-spec/auth4d-260.md)) applies, the page runs the same verified
email step as hosted login (auth4d-263.md (repository file docs/api-spec/auth4d-263.md), auth4d-264.md (repository file docs/api-spec/auth4d-264.md)) before
the browser is signed in. The device code can only be approved once the step is complete. The
tokens the device then polls carry email, email_verified: true and idp. The design is in
docs/architecture/verified-email-step.md (repository file docs/architecture/verified-email-step.md).
Guest upgrades run the step since AUTH4D-302 (auth4d-302.md (repository file docs/api-spec/auth4d-302.md)).
POST /device/sign-in/resume
The request is unchanged. When the redeemed proof’s sign-in needs the step, the proof is still
consumed, but no browser session is created. The answer (DeviceSignInResumeResponseSchema) is:
{
"status": "verify_email",
"user_code": "ABCD1234",
"interaction": { "type": "verify_email", "step": "est_…", "expires_in": 600 }
}
It sets the __Host-auth4_email_step cookie and leaves the device sign-in flow cookie
(__Host-auth4_flow_{browser_flow_id}) in place. The step is stored in the tenant’s Durable
Object for ten minutes, bound to the device sign-in flow and the user. Every other answer is as
before; signed_in still means the browser is signed in.
Step endpoints
POST /t/{tenantId}/email-step/{state,send,resend,confirm,abandon} take the native application’s
client_id. A device step answers only for that native application, and an authorization step only
for a browser application; any other pairing answers 401 unauthorized_client.
-
confirmproves the code and attaches the address (or links it underafter_code) as in auth4d-264.md (repository filedocs/api-spec/auth4d-264.md). It then signs the browser in for the device sign-in flow as the final user. It needs the flow cookie as well as the step cookie. The answer is200 {"status": "signed_in", "user_code": "ABCD1234"}(DeviceEmailStepSignedInSchema). It sets__Host-auth4_sessionand clears the step and flow cookies. Nothing is approved yet: the page shows the approval and the player confirms it with/device/approval.Status Body When 400 {"error":"expired_token"}The device code expired or was used meanwhile; the address stays saved 403 {"error":"access_denied"}No flow cookie, the application was disabled or the step still applies -
abandonends the step without approving or denying the code. The device keeps polling and getsauthorization_pending. The answer is{"redirect_to": "{AUTH_ORIGIN}/login/device?tenant_id=…&client_id=…"}, with the step cookie cleared.restartis accepted and ignored. The hosted page shows its sign-in methods again for the same code in place.
GET /device/verify and POST /device/approval
A browser session whose user still owes the step can’t approve the code. /device/verify answers
decision: "login_required" with the sign-in methods, and /device/approval answers
401 {"error":"login_required"}. This covers a session from elsewhere, or one created before the
application turned the step on. A session the device page signed in uses the facts of its own
sign-in (method, upstream email status). Any other session is judged as a reused session, as
/oauth/authorize/continue does. /device/denial never needs an address.
Device-grant tokens
When the device polls /oauth/token, the tokens follow the AUTH4D-265 rule. With the email scope
and an Auth4-verified address, the ID token carries the lower-cased email and
email_verified: true, and it never carries email: null or email_verified: false. When the
device page signed the approving browser in, the ID token carries the idp of that sign-in (for
example steam). sub never changes.
Audit
The step writes the same email_step.* events as on hosted login, with the native client_id and
the sign-in’s idp, and no code or whole address.
Source: docs/api-spec/auth4d-269.md