Oauth
Eternaltwin uses the OAuth 2.0 authorization framework to expose its data to the game websites. Each game website is an OAuth 2 client.
Client creation
The first step is to create an OAuth client. It is not exposed publicly yet.
The creation options are:
- General information: display name, homepage URI
- Technical: OAuth redirect (callback) URI
The server creates the application and picks a random secret. The secret is returned as clear-text in the creation response, it is never returned again. The server treats the secret as a password and only stores its hash.
Authorization request
To authenticate a user, the client must redirect the end user to Eternaltwin to request its authorization.
The base URI is https://eternaltwin.org/oauth/authorize, with the following query parameters:
| Name | Description |
|---|---|
| client_id | Required. Client ID received during the client creation |
| redirect_uri | Redirection URI. The default and only accepted value is the one configured during the app creation |
| login | Not yet implement: username to use by default |
| scope | Not yet implement: scopes to request |
| state | A string returned as-is |
| method | Authentication method suggestion if the user needs to sign-in (etwin, twinoid, hammerfest). Default: etwin |
Parameters
State
The state parameter is a string returned as-is once the user has authenticated. It must include an unguessable part to prevent CSRF attacks.
For the system clients, we use a JWT based on this RFC draft for the state. In particular, it has a "Request Forgery Protection" (rfp) field.
Redirect URI
Eternaltwin does not allow dynamic redirect URIs. Use the state parameter to encode state.
For the system clients, we use https://<game>/oauth/callback if the client supports only one authorization server (Eternaltwin), or https://<game>/oauth/callback/<as> where as is a string identifying the authorization server.
Access token request
Once the user is redirected back to the client callback, the client exchanges the authorization
code for an access token with POST https://eternaltwin.org/oauth/token, authenticating itself
with its client id and secret.
Authorization codes are valid for 10 minutes.
Errors
Errors are not reported in the RFC 6749 format:
there is no error / error_description pair. Eternaltwin replies with an HTTP error status and
its own JSON error object:
{"code": "Q1012", "message": "the user has not accepted the terms of service", "extra": null}
A generic OAuth library only reports the HTTP status for these; read the code field to tell them
apart. The Node client exposes them as ErrorCode (@eternaltwin/client-node), and
RfcOauthClient#getAccessToken (@eternaltwin/oauth-client-http) parses the body into
GetAccessTokenError#eternaltwin.
| Code | Status | Meaning |
|---|---|---|
Q0002 | 401 | Missing or invalid client authentication |
Q1008 | 422 | Missing code parameter |
Q1010 | 422 | Malformed authorization code |
Q1011 | 422 | Expired (or not yet valid) authorization code |
Q1009 | 403 | The code was issued for a different client |
Q1012 | 403 | The user has not accepted the terms of service |
S0000 | 500 | Internal error |
Terms of service (Q1012)
A user who has not accepted the Eternaltwin terms of service can not take part in an OAuth grant.
/oauth/authorize redirects such a user to the acceptance page and comes back to the authorization
request once they accept, so a browser flow normally never reaches this error. It is still returned
by /oauth/token for codes obtained some other way (a code issued before the user's acceptance was
revoked, a non-browser flow, a replayed code).
Clients receiving Q1012 should:
- treat it as final and user-actionable, not transient: do not retry automatically. The code stays claimable for the rest of its 10 minutes, but retrying is pointless until the user acts.
- abort the sign-in without creating a local account or session: there is no access token, hence no identity.
- tell the user their Eternaltwin account must accept the terms of service, link them to
https://eternaltwin.org/tos-accept, and offer to restart the flow at
/oauth/authorize.
Access tokens issued before this check existed are unaffected: acceptance is only verified when a token is granted, never when it is used.