Skip to content

Instantly share code, notes, and snippets.

@Henrik-3
Last active September 12, 2024 08:20
Show Gist options
  • Save Henrik-3/d6b631fb7c61821bc16b17cd347a3811 to your computer and use it in GitHub Desktop.
Save Henrik-3/d6b631fb7c61821bc16b17cd347a3811 to your computer and use it in GitHub Desktop.
RSO

Implementing RSO

How long do i have to wait for my application being reviewed

Depends. Thats for example based on holidays in the US and other factors that could affect the review time for applications. Also there is a difference between a raw RSO apply for LOL and a full production key apply for valorant for example, the second one could potential take longer then the first option. The range can be 12 hours up to 3-4 months

What information Riot needs from you to create your RSO client?

(You have to send that information after your RSO got accepted, so make sure you have preperared the following things)

  1. Contact Emails
  2. Name of the company/service
    • Will also appear on the RSO Login Page
  3. Logo of the company/service
    • Should scale between 275x275 and 60x60
    • Suggested is an SVG, but PNG will also work
    • Will be displayed on light and dark backgrounds (see examples below)
  4. Privacy policy URL
  5. Terms of service URL
  6. Redirect URLs
    • A list of URLs you would like the RSO Client to redirect the players after login
    • Typically a callback which is used to get information about the player who logged in
  7. Post Logout Redirect URLs
    • A list of URLs you would like the RSO Client to redirect the players after logout
  8. Preffered ClientId
    • Unique name for your product without punctation or spaces

image image

Things you have to do soon after receiving the creation email

  • You will receive a link to gather your client secret or your client_assertion
    • This link will expire after 6 days after you got the mail or after three times opening the link
    • This link is secured with a basic auth, the credentials for this will be also in the mail

Implementing RSO

Endpoints you can access with RSO

Endpoint Description
GET https://auth.riotgames.com/authorize Endpoint for obtaining an authorization code
POST https://auth.riotgames.com/token Endpoint to exchange authorization codes for access, identity, and refresh tokens
GET https://auth.riotgames.com/jwks.json Endpoint to grab JSON Web Keys for verifying the authenticity of access and identity tokens
GET https://auth.riotgames.com/userinfo Endpoint to use your access token to obtain user information
GET https://{cluster}.api.riotgames.com/riot/account/v1/accounts/me Endpoint for optaining gameName, tagLine and PUUID for VALORANT and Legends of Runeterra. Note: The data that is returned from each cluster is identical. Use the cluster closest to your handling server. Clusters: americas, europe, asia. Docs: Link
GET https://{cpid}.api.riotgames.com/lol/summoner/v4/summoners/me Endpoint for optaining accountId, profileIconId, revisionDate, name, id, puuid and summonerLevel for for League Of Legends. Note: You have to include the cpid scope in the login url. You can optain the cpid by doing a request to the userinfo endpoint. Docs: Link
GET https://{cpid}.api.riotgames.com/tft/summoner/v1/summoners/me Endpoint for optaining accountId, profileIconId, revisionDate, name, id, puuid and summonerLevel for for Teamfight Tactics. Note: You have to include the cpid scope in the login url. You can optain the cpid by doing a request to the userinfo endpoint. Docs: Link

Understanding the Authorization Code Flow

After the player logged in with the correct credentials, they are automaticly redirected to the redirect_uri you specified, together with an authorization code as a query string in the url. This code can be sent to the token endpoint to receive access, identity, and refresh tokens. The access token you receive from this endpoint can be then used to get more sensitive data like summoner information, locale, puuid and some more stuff

Sending Users to your RSO Page

Here are the fields you can or have to include in your RSO URL

Field Description Optional
redirect_uri OAuth 2 Callback route you have to set up at your own server. This route needs to be able to process a code query parameter that is added to the URI on when Riot Sign On redirects the player back to our URI. We must also be sure we have this URI added as one of the redirect_uris during client registration See Bullet Point 6 here
client_id ID assigned to client during registration. This will be the Client ID you got when you registered a client See Bullet Point 8 here
response_type Response type expected, should be code for authorization code flow
scope A predefined data scope, must include openid to authenticate and cpid if you use RSO for LoL or TfT
ui_locales Space-separated list of player’s preferred BCP47 language tag values in order of most to least preferred
state An opaque value provided to the authorize endpoint. The same value is returned to you when the endpoint sends its reply. Enables you to compare value sent and received, to prevent CSRF
prompt Can only be "login". Will show the login page where you can switch accounts or have to login again with your password

Here are the additional scopes you can use

Field Description Optional Useable?
cpid Return the game region for League of Legends
offline_access Allows refresh tokens to be used to retrieve new access_tokens that have access to the /userinfo endpoint
account ???
email Returns the email of the account
profile ???

If you add all fields that are needed together you will get the following result:

https://auth.riotgames.com/authorize?redirect_uri=http://example.com/callback&client_id=exampleclientid&response_type=code&scope=openid

Response from RSO

When the player successfully logs in, a 302 Redirect sends their browser to the redirect_uri that you included in your Sign In link.

This route receives a code as a url query-string parameter, and the server must then make a server-to-server request to exchange this code for access, identity, and refresh tokens. We’ll need to send a few things to Riot Sign On’s token endpoint to get these tokens back. The endpoint which is used here is the token endpoint.

Field Value Description
Authorization "Basic " + Base64Encode(client_id + ":" + client_secret) Authorization Header
grant_type (Form Data) "authorization_code" Grant type
code (Form Data) Individual per request (is a string) RSO access code, which we received as a querystring parameter to our oauth2-callback route
redirect_uri (Form Data) Same redirect URL that is passed in the login link RSO access code, which we received as a querystring parameter to our oauth2-callback route

Sample Response

{  
  "scope":"openid",
  "expires_in":600,
  "token_type":"Bearer",
  "refresh_token":"dXJuOnJpb3Q6cOk1qdNal...8zN3NzbQ.xw96rZeGEMtrFlDCGLyA",
  "id_token":"eyJhbGciJSUzI1mtpZCInMxIn0...YiI6InVybjpyaW90OpZDp2MTpNalV",
  "sub_sid":"vldfsXGdDPoafSKfjS932cslKu8JDUKZ-woZvXDoq8",
  "access_token":"eyJhbGciOi1NsImZCI6InM...NTkzMTA3LCJjaWQiJnmE-BVnZbYqY"
}

Explanation of all fields from the response

Field Description
scope Details what level of access the given Access Token provides. See the scopes list for more information
expires_in Life span of the access token
token_type Method of authorization token provides. Bearer means the entire token should be provided
sub_sid The identifier of an existing session (SID) for the subject (player)
access_token Undecryptable JWT Token. Used for scoped authentication of a client and player to a resource
id_token Decryptable JWT Token. Provides information to authenticate a player’s identity
refresh_token Issued for the purpose of obtaining new access tokens when an older one expires

Using Tokens and Verification

Using Tokens Endpoint (With a JWT Token, new method)

POST https://auth.riotgames.com/token

Headers:
    - None needed
Form Data:
    - client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
    - client_assertion: SIGNED-JWT-HERE
    - grant_type: "authorization_code"
    - redirect_uri: REDIRECT-URL-HERE
    - code: CODE-FROM-QUERY-STRING-HERE

JavaScript WebServer Example

const axios = require('axios');
const fastify = require('fastify')();
const example_client_assertion = 'SIGNED-JWT-HERE';

fastify.get('/api/v1/rso/login', async (req, res) => {
    const formData = new URLSearchParams();
    formData.append('client_assertion_type', 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer');
    formData.append('client_assertion', example_client_assertion);
    formData.append('grant_type', 'authorization_code');
    formData.append('code', req.query.code);
    formData.append('redirect_uri', 'https://example.com/api/v1/rso/login');
    const tokens = await axios.post('https://auth.riotgames.com/token', formData).catch(error => console.error(error));
    console.log(tokens.data);
});

fastify.listen(3000, () => {
    console.log('API Online');
});

Using Tokens Endpoint (with client secret, old method)

POST https://auth.riotgames.com/token

Headers:
    - Authorization: "Basic + Base64Encode(client_id + ":" + client_secret)"
Form Data:
    - grant_type: "authorization_code"
    - code: CODE-FROM-QUERY-STRING-HERE
    - redirect_uri: REDIRECT-URL-HERE

JavaScript WebServer Example

const axios = require("axios")
const fastify = require("fastify")()
const examplesecret = "000000000000000000000"

fastify.get("/api/v1/rso/login", async (req, res) => {
    const formData = new URLSearchParams()
    formData.append('grant_type', "authorization_code")
    formData.append('code', req.query.code)
    formData.append('redirect_uri', "https://example.com/api/v1/rso/login")
    const tokens = await axios.post("https://auth.riotgames.com/token", formData, {
        headers: {
            "Authorization": `Basic ${Buffer.from(`exampleclientid:${examplesecret}`).toString("base64")}`
         }
    }).catch(error => console.error(error))
    console.log(tokens.data)
})

fastify.listen(3000, () => {console.log("API Online")})

Response

{  
  "scope":"openid",
  "expires_in":600,
  "token_type":"Bearer",
  "refresh_token":"dXJuOnJpb3Q6cOk1qdNal...8zN3NzbQ.xw96rZeGEMtrFlDCGLyA",
  "id_token":"eyJhbGciJSUzI1mtpZCInMxIn0...YiI6InVybjpyaW90OpZDp2MTpNalV",
  "sub_sid":"vldfsXGdDPoafSKfjS932cslKu8JDUKZ-woZvXDoq8",
  "access_token":"eyJhbGciOi1NsImZCI6InM...NTkzMTA3LCJjaWQiJnmE-BVnZbYqY"
}

Notes

None

Using Refresh Tokens

POST https://auth.riotgames.com/token

Headers:
    - Authorization: "Basic + Base64Encode(client_id + ":" + client_secret)"
Form Data:
    - grant_type: "refresh_token"
    - refresh_token: TOKEN_HERE
    - scope (optional): SCOPE-LIST

JavaScript WebServer Example

const axios = require("axios")
const refresh_token = "000000000000000000000"

const formData = new URLSearchParams()
formData.append('grant_type', "refresh_token")
formData.append('refresh_token', refresh_token)
const tokens = await axios.post("https://auth.riotgames.com/token", formData, {
    headers: {
        "Authorization": `Basic ${Buffer.from(`exampleclientid:${examplesecret}`).toString("base64")}`
     }
}).catch(error => console.error(error))
console.log(tokens.data)

Response

{  
    "scope":"openid",
    "expires_in":600,
    "token_type":"Bearer",
    "refresh_token":"dXJuOnJpb3Q6cGlkOn...amNvaG8zN3NzbQeGEmeMtrFlDCGLyA",
    "access_token":"eyJhbGciOiJSUzI1NiI...sFwkadLmWmwtvJouhX22Tc6vPnfXTk"
}

Notes

When using a refresh token, there are two actions RSO may take. 
If it replies with a new refresh token, it must be used in all future access token refreshes and the previous refresh token is now invalid.
If the same refresh token is received in the response, you may continue to use it in future refresh requests.

Using ID Tokens

Included in the [Using Token Endpoint](#using-tokens-endpoint) response. No requested needed.

JavaScript WebServer Example

const axios = require("axios")
const fastify = require("fastify")()
const examplesecret = "000000000000000000000"

fastify.get("/api/v1/rso/login", async (req, res) => {
    const formData = new URLSearchParams()
    formData.append('grant_type', "authorization_code")
    formData.append('code', req.query.code)
    formData.append('redirect_uri', "https://example.com/api/v1/rso/login")
    const tokens = await axios.post("https://auth.riotgames.com/token", formData, {
        headers: {
            "Authorization": `Basic ${Buffer.from(`exampleclientid:${examplesecret}`).toString("base64")}`
         }
    }).catch(error => console.error(error))
    const userdata = JSON.parse(Buffer.from(tokens.data.id_token.split('.')[1], "base64"))
})

fastify.listen(3000, () => {console.log("API Online")})

Response

{
    "sub": "000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "aud": "exampleclientid",
    "acr": "urn:riot:bronze",
    "amr": [ "google_auth" ],
    "iss": "https://auth.riotgames.com",
    "exp": 1645963629,
    "locale": "de_DE",
    "iat": 1645877229
}

Notes

None
@bpystep
Copy link

bpystep commented May 28, 2024

How can I register a client? Where can I get that form?

https://beta.developer.riotgames.com

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment