Skip to main content

Use OAuth 2.0 for authentication

Overview

Sky Mavis's OAuth 2.0 APIs can be used for both authentication and authorization. This page describes our OAuth 2.0 implementation for authentication, which complies with the OpenID Connect (OIDC) specification.

By using the Sky Mavis implementation of OIDC, you can delegate sign-up, sign-in, and other identity management activities in your app to the Mavis Account service.

The instructions in this document show how to do so regardless of the programming language, by sending and receiving HTTP requests.

Quickstart

Explore Mavis Account authentication in a sample app on GitHub: github.com/axieinfinity/oidc-quickstart/.

Prerequisites

  • An app in the Developer Console.
  • Configure OAuth 2.0 settings as described in the Get started guide.

Authenticate the user

Authenticating the user involves obtaining an ID token and validating it. ID tokens are a standard feature of OIDC used for sharing identity assertions on the internet.

The most commonly used approach for authenticating users is called the "authorization code" flow. This flow allows the backend server of an app to verify the identity of the person using a browser or mobile device. This document describes how to perform the authorization code flow for authenticating the user.

Authenticating your requests

To authenticate requests to our OAuth 2.0 APIs, you can send your API key in the X-API-Key request header:

POST /account/oauth2/token
Host: https://api-gateway.skymavis.com
Content-Type: application/x-www-form-urlencoded
X-API-Key: abcdef12345

Or in the query string:

POST /account/oauth2/token?apikey=abcdef12345

When a user tries to sign in with Mavis Account, you need to do the following:

  1. Send an authentication request to Sky Mavis
  2. Exchange code for an access token and ID token
  3. Obtain user information from the ID token
  4. Authenticate the user

1. Send an authentication request

Form an HTTPS GET request with the appropriate URL parameter. You should retrieve the base URL from the discovery document using the authorization_endpoint metadata value. This section assumes the base URL is https://api-gateway.skymavis.com/oauth2/auth.

In your request, specify the following parameters:

  • client_id: the client ID obtained from the Developer Console.
  • redirect_uri: the HTTP endpoint on your server that receives the response from Sky Mavis. The value must exactly match the authorized redirect URI for the given client_id specified in the Developer Console. If this value doesn't match an authorized URI, the request fails with a redirect_uri_mismatch error.
  • response_type: the response type must be set to the code value in the authorization code flow.
  • scope: the scope must be set to the openid value.
  • nonce: a random value generated by your app that enables replay protection.
  • state: an opaque value used to maintain state between the request and the callback. For more information, see Authorization code flow.
  • login_hint: when your app knows which user it's trying to authenticate, it can provide this parameter as a hint to the authentication server. Passing this hint suppresses the account chooser and either pre-fills the email box on the sign-in form, or selects the proper session. The value can be either an email address or the sub string, which is equal to the user's Mavis ID. If you don't provide a login_hint and the user is logged in, the consent screen includes a request for approval to release the user's email address to your app.

Example request:

GET /oauth2/auth?
Host: https://api-gateway.skymavis.com

client_id={client_id}
&redirect_uri={redirect_uri}
&response_type=code
&state=random_string
&scope=openid

2. Get an access token and ID token

The response includes a code parameter, a one-time authorization code that your server can exchange for an access token and ID token.

To make this exchange, send an HTTPS POST request to the token endpoint, which you can retrieve from the Discovery document using the token_endpoint metadata value. This section assumes the endpoint is https://api-gateway.skymavis.com/oauth2/auth.

In the body of your request, specify the following parameters:

  • code: the authorization code returned from the authentication request.
  • client_id: the client ID obtained from the Developer Console.
  • client_secret: the client secret obtained from the Developer Console.
  • redirect_uri: an authorized redirect URI for the given client_id specified in the Developer Console. If this value doesn't match an authorized URI, the request fails with a redirect_uri_mismatch error.
  • grant_type: this field must contain the value authorization_code as defined in the Developer Console.

Here's an example request with the parameters described:

POST /account/oauth2/token
Host: https://api-gateway.skymavis.com
Content-Type: application/x-www-form-urlencoded
X-API-Key: {your_api_key}

grant_type=authorization_code
&code={authorization_code}
&redirect_uri={your_callback_url}
&client_id={your-client-id}
&client_secret={your-client-secret}

A successful response to this request contains the following parameters in a JSON array:

  • access_token: a token that can be sent to a Sky Mavis API.* expires_in: the remaining lifetime of the access token in seconds.
  • id_token: a JSON Web Token (JWT) that contains identity information about the user digitally signed by Sky Mavis.
  • scope: the scopes of access granted by the access_token expressed as a list of space-delimited, case-sensitive strings.
  • token_type: identifies the type of token returned. This field always has the value Bearer.

3. Get user information from the ID token

An ID token is a JSON Web Token (JWT)—a cryptographically signed Base64-encoded JSON object. Normally, you'd have to validate a token before using it, but because you're communicating directly with Sky Mavis over an intermediary-free HTTPS channel and are using your client secret to authenticate yourself to Sky Mavis, you can be confident that the token you receive really comes from Sky Mavis and is valid.

If your server passes the ID token to other components of your app, then the other components must validate the token before using it.

An ID token is a JSON object containing a set of name-value pairs. Consider this example, formatted for readability:

{
"acr": "google",
"amr": [
"oidc"
],
"at_hash": "7Y1j0a-pn_RQXEW4vsLkHw",
"aud": [
"{client_id}"
],
"auth_time": 1683283201,
"email": "test@skymavis.com",
"exp": 1683888020,
"iat": 1683283220,
"iss": "https://athena.skymavis.com/",
"jti": "06934da0-03cc-483b-9d78-eb7ff143aec9",
"name": "test user",
"nonce": "1683283184931",
"rat": 1683283185,
"redirect_uri": "http//localhost/callback",
"roninAddress": "0x000000",
"sid": "5c4feba2-02ec-49cd-9d6c-257b29c57e2f",
"sub": "{mavis_id}",
"walletConnect": null
}

4. Authenticate the user

After obtaining user information from the ID token, you need to query your app's user database. If the user already exists in the database, start an app session for that user if all login requirements are met by the Sky Mavis API response.

If the user doesn't exist in your user database, then redirect the user to your new-user sign-in flow. You may be able to auto-register the user based on the information you receive from Sky Mavis, or at the very least you may be able to pre-populate many of the fields that you require on your registration form. In addition to the information in the ID token, you can get other user profile information at the user info endpoint https://api-gateway.skymavis.com/account/userinfo.

Example request:

HTTP
GET /account/userinfo
Host: https://api-gateway.skymavis.com
X-API-Key: {your_api_key}
Authorization: Bearer {access_token}

Log out the user

The logout process refers to terminating the user's session and revoking the app's access to the user's resources. Logging out is performed on both the client app and the identity provider.

For a logout request, specify the following parameters:

  • apikey: the API key obtained from the Developer Console.
  • id_token_hint: a hint about the user's current ID token, helping the server identify the correct session. Replace id_token_issued_to_client with the actual ID token
  • post_logout_redirect_uri: a URI for redirecting the user's browser after logout. Must be registered in the Developer Console.
  • state: a random string generated by the client app to maintain a secure state between the logout request and callback.

Example request:

GET /account/oauth2/sessions/logout?
Host: https://api-gateway.skymavis.com

apikey={apikey}
&id_token_hint={id_token_issued_to_client}
&post_logout_redirect_uri={post_logout_redirect_uri}
&state=random_string

Advanced topics

The following sections describe the Sky Mavis OAuth 2.0 API in greater detail. This information is intended for developers with advanced requirements around authentication and authorization.

Authorization code flow with PKCE

Authorization code flow with PKCE (Proof Key for Code Exchange) is an OAuth 2.0 authentication and authorization mechanism designed to improve the security of the authorization code flow.

The authorization server uses the same hash function to derive a code challenge from a code verifier and compares it to the code challenge it stored during the initial authorization request.

PKCE is a security extension to the authorization code flow that mitigates this attack by adding an additional code_verifier parameter to the initial authorization request. The client generates a code_verifier, which is a cryptographically random string of characters, and uses it to derive the code_challenge parameter using a one-way hash function. The code_challenge is sent along with the authorization request to the authorization server, which stores it.

The new parameters in PKCE compared to the standard authorization code flow are as follows:

  • code_verifier: a random string generated by the client app. It is used to create the code_challenge and is later sent to the server when exchanging the authorization code for an access token.
  • code_challenge: creates a SHA256 hash of the code_verifier and encodes is using Base64URL. The code_challenge is sent in the initial authentication request.
  • code_challenge_method: the method used to create the code_challenge from the code_verifier. Typically, the method used is S256 (SHA-256 hashing), but it can also be plain (no hashing).

Generate a code verifier

Create a random string of length between 43 and 128 characters using characters from the sets [A-Z], [a-z], [0-9], -, ., \_, ~.

export function generateRandomString(length: number): string {
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~'
let result = ''
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * characters.length))
}
return result
}

Generate a code challenge

Create a code challenge from the received code_verifer.

function base64UrlEncode(buffer: Buffer) {
let base64 = buffer.toString('base64')
base64 = base64.replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_')
return base64
}

export function generateCodeChallenge(codeVerifier: string) {
// Encode the code_verifier as bytes
const codeVerifierBytes = Buffer.from(codeVerifier, 'ascii')

// Hash the code_verifier using SHA-256
const codeChallengeBytes = crypto.createHash('sha256').update(codeVerifierBytes).digest()

// Encode the hashed code_verifier as Base64
const codeChallenge = base64UrlEncode(codeChallengeBytes)

return codeChallenge
}

To implement PKCE in your authorization process, when making the initial authentication request, include the code_challenge and code_challenge_method as query parameters. When exchanging the authorization code for an access token, include the code_verifier as a parameter in the request.

The authorization server then verifies the code_verifier and code_challenge pair before issuing an access token, ensuring that the client app making the request is the same one that initiated the authentication process.

An example initial authentication request with PKCE:

GET /oauth2/auth?
Host: https://api-gateway.skymavis.com

client_id={client_id}
&redirect_uri={redirect_uri}
&response_type=code
&state=random_string
&scope=openid
&code_challenge={code_challenge}
&code_challenge_method=S256

An example access token request with PKCE:

POST /account/oauth2/token
Host: https://api-gateway.skymavis.com
Content-Type: application/x-www-form-urlencoded
X-API-Key: {your_api_key}

grant_type=authorization_code
&code={authorization_code}
&redirect_uri={your_callback_url}
&client_id={your-client-id}
&client_secret={your-client-secret}
&code_verifier={code_verifier}

Implicit flow

Security warning

The implicit flow returns the access token directly and doesn't require security codes like the authorization code flow. If your app needs higher security, consider implementing the authorization code flow as a safer authentication method.

The implicit flow is mainly used by clients implemented in a browser using a scripting language. The access token and ID token are returned directly to the client, which may expose them to the end user and apps that have access to the end user's user agent. The authorization server does not perform client authentication.

When using the implicit flow, the authorization endpoint https://api-gateway.skymavis.com/oauth2/auth is used in the same way as for the authorization code flow.

The difference between the implicit flow and authorization code flow lies in how the access token is obtained. In the implicit flow, the access token is returned directly from the authorization server to the client after the user has successfully authenticated, whereas in the authorization code flow, the authorization server returns an authorization code to the client, and then the client uses this authorization code to exchange for an access token.

Form an authentication request with the following parameters:

  • response_type: OAuth 2.0 response type value that determines the authorization processing flow to be used, including what parameters are returned from the endpoints used. When using the implicit flow, this value is id_token token or id_token. |
  • redirect_uri: an authorized redirect URI for the given client_id specified in the Developer Console. When using the implicit flow, don't use the HTTP scheme unless the client is a native app, in which case it may use the HTTP scheme with localhost as the hostname.
  • nonce: a random value generated by your app that enables replay protection when present.

Example initial authentication request:

GET /oauth2/auth?
Host: https://api-gateway.skymavis.com

client_id={client_id}
&redirect_uri={redirect_uri}
&response_type=id_token
&state=random_string
&scope=openid
&nonce={nonce}

If your response_type is either id_token or id_token token and no error occurs, the system returns the following parameters:

  • access_token: only returned if response_type included a token.
  • expires_in: number of second until the expiration of the access token.
  • id_token: an ID token of the authorized user.
  • token_type: type of the access token. The value is always Bearer.
  • scope: scopes of the access token. These might differ from the provided scope parameter.
  • state: unmodified state parameter from the request.

Resource owner password credentials flow

The resource owner password credentials (ROPC) flow allows an app to sign in the user with the Ronin Wallet extension or mobile Ronin Wallet app without opening a web browser. This flow is therefore also known as "Sign-In with Ronin Wallet." The flow describes how Ronin Wallet authenticates with Sky Mavis Account by signing a standard message format parameterized by scope, session details, and security mechanisms, such as a nonce.

Send a fetch nonce request

In the ROPC flow, the app first needs to send a fetch nonce request to the authorization server.

curl 'https://${DOMAIN}/account/oauth2/ronin/fetch-nonce?address={{ronin_address}}

The message must be generated following the ERC-4361: Sign-In with Ethereum standard with the format as follows:

${domain} wants you to sign in with your Ronin account:
${address}

${statement}

URI: ${uri}
Version: ${version}
Chain ID: ${chain-id}
Nonce: ${nonce}
Issued At: ${issued-at}
Expiration Time: ${expiration-time}
Not Before: ${not-before}
Request ID: ${request-id}
Resources:
- ${resources[0]}
- ${resources[1]}
...
- ${resources[n]}

Example nonce response:

{
"nonce": "17924493584421707055",
"issued_at": "2023-06-06T04:41:08Z",
"not_before": "2023-06-06T04:41:08Z",
"expiration_time": "2023-06-06T04:41:38Z"
}

Generate a message with the nonce

Generate a message with the nonce following EIP-4361.

Example message:

developers.skymavis.com wants you to sign in with your Ronin account:
0x6ca...8116

I accept the ServiceOrg Terms of Service: https://developers.skymavis.com/tos

URI: https://developers.skymavis.com/dashboard/login
Version: 1
Chain ID: 2020
Nonce: 17924493584421707055
Issued At: 2023-06-06T04:41:08Z
Expiration Time: 2023-06-06T04:41:38Z
Not Before: 2023-06-06T04:41:08Z

User signs the message

The user uses their Ronin Wallet to sign the message, after which the message is returned to the client app.

Authenticate with the message and signature

Send an authentication request containing the message and the signature:

curl --request POST \
--url 'https://{DOMAIN}/account/oauth2/token' \
--header 'content-type: application/x-www-form-urlencoded' \
--data grant_type=ronin \
--data message={message} \
--data signature={signature} \
--data 'client_id={yourClientId}' \
--data 'client_secret={yourClientSecret}' \
--data 'scope=openid offline'

Token response:

{
"id_token": "eyJ...i",
"access_token": "eyJ...i",
"expires_in": 600,
"scope": "openid offline",
"token_type": "bearer"
}

Token exchange

Token exchange is a process in the authorization code flow where your app redeems the authorization code for access token and refresh token from the authorization server. For this to work, the app needs to authenticate with the authorization server through the client_secret_post token endpoint authentication method.

This authentication method sends the client ID and client secret in the body of the POST request to the authorization server. For example:

POST /account/oauth2/token
Host: https://api-gateway.skymavis.com
X-API-Key: {your_api_key}
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code={authorization_code}
&redirect_uri={redirect_uri}
&client_id={client_id}
&client_secret={client_secret}
PKCE flow

The code_verifier parameter is required in requests within the PKCE flow.

Refresh token

Refresh token is a token used to extend the life of the access token without the user having to re-authenticate. In the authorization code flow, after the app redeems the authorization code for the access token and refreshes the token, the app can use this refresh token to get a new access token when the old access token expires.

Similar to token exchange, when using the refresh token, the app needs to authenticate with the authorization server through the client_secret_post token endpoint authentication method. The following sections describe how to use the refresh token for theclient_secret_post method.

This authentication method sends the client ID and client secret in the body of the POST request to the authorization server. For example:

POST /account/oauth2/token
Host: https://api-gateway.skymavis.com
X-API-Key: {your_api_key}
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token
&refresh_token={refresh_token}
&scope={scope}
&client_id={client_id}
&client_secret={client_secret}

Discovery document

Authenticating users and requesting resources, such as tokens, users information, and public keys, require multiple endpoints under OIDC.

OIDC uses an entity called a "Discovery document" to simplify implementations and increase flexibility. This Discovery document is a JSON document that contains key-value pairs providing information about the OIDC provider's configuration, such as the URIs of the authorization, token, revocation, userinfo, and public-key endpoints.

You can retrieve the discovery document for Sky Mavis's OIDC services from the following URL: https://api-gateway.skymavis.com/account/.well-known/openid-configuration.

To use Sky Mavis's OIDC services, you need to hard-code the discovery document URI into your app. Your app fetches the document, applies caching rules in the response, then retrieves endpoint URIs from it as needed.

Was this page helpful?
Happy React is loading...