6 min read

Source: Roblox Creator Hub · CC BY 4.0 · View source · Code samples: MIT Imported 2026-10-03. Formatting adapted for this site.

OAuth 2.0 app implementation

Open Cloud supports the OAuth 2.0 authorization flow with and without PKCE, depending on whether your clients are confidential or public.

We recommend the OAuth 2.0 authorization code flow with PKCE for all clients and require it for public clients.

Note

Store the client secret in a secure place and never expose it to the public. Bad actors can use it to impersonate your application.

To implement the authorization code flow, your app performs the following steps:

  1. Generate a code verifier and code challenge (PKCE only). This lets you include a challenge in your requests rather than the client secret, which improves security.
  2. Send a GET request to the authorization endpoint with the appropriate parameters.
  3. Handle the authorization callback. After obtaining authorization, use the authorization code from Roblox in a redirect request to the URL you specified during app creation. Parse the authorization code for later use.
  4. Send a POST request to the token endpoint with the authorization code. If successful, you receive access and refresh tokens with which to make API calls.
  5. Call any Open Cloud API that supports OAuth 2.0 authentication based on the reference documentation for the resources you want to access.

The following sections describe each step in greater depth.

Generate a code challenge (for PKCE only)

Before initiating the authorization process, you need to generate a code challenge from a code verifier. You can use libraries or built-in functions in the programming language of your choice to create the code verifier, calculate the hash, and perform Base64 encoding to generate the code challenge.

When creating the code verifier, only use unreserved characters, including uppercase and lowercase letters (A-Z, a-z), decimal digits (0-9), hyphen (-), period (.), underscore (_), and tilde (~), with a minimum length of 43 characters and a maximum length of 128 characters.

The following example shows how to use Javascript to create a code verifier and generate the code challenge using the SHA-256 hashing algorithm:

const crypto = require('crypto');

// base64URL encode the verifier and challenge
function base64URLEncode(str) {
  return str.toString('base64')
      .replace(/\+/g, '-')
      .replace(/\//g, '_')
      .replace(/=/g, '');
}

// create sha256 hash from code verifier
function sha256(buffer) {
  return crypto.createHash('sha256').update(buffer).digest(`base64`);
}

// create a random code verifier
var code_verifier = base64URLEncode(crypto.randomBytes(32));
// generate a challenge from the code verifier
var code_challenge = base64URLEncode(sha256(code_verifier));

For PKCE, you need both the code verifier and challenge values in later steps.

Construct the authorization URL

Note

See the authorization endpoint reference documentation for complete information on how to construct the authorization URL.

To initiate the user authorization process, construct an authorization URL with the required parameters:

Required for PKCE only

Required for non-PKCE only

https://apis.roblox.com/oauth/v1/authorize?client_id=7290610391231237934964
    &code_challenge=PLEKKVCjdD1V_07wOKlAm7P02NC-LZ_1hQfdu5XSXEI
    &code_challenge_method=S256
    &redirect_uri=https://example.com/redirect
    &scope=openid%20profile
    &response_type=code
    &state=abc123

When users visit the URL, they are taken through the authorization flow. If successful, Roblox redirects the user to the specified redirect_uri.

Handle authorization callbacks

When an authorization flow is successful, your app receives a GET request from the Roblox authorization server at the redirect_uri that you specified. In the request, you receive code (authorization code) and state(if you provided a value previously) parameters.

For example, you might receive a GET request with the following URL if you specified your redirect URI to be https://example.com/redirect.

https://example.com/redirect?code=10c45PNuquKnFJ6pUcy5-fHkghEM6lSegm-7hj9mVEprub1dSDuStuKK_EAUXY7AHTD63xcnmvxSLthp-C8g3jzIGZVzuXSd20Y2dEYI9hx0LZmPg95ME4z2K03UheiZbroyXUjYyB3ReoMqobzDVPzyx6IS8kj2Uu-11Xq_0JiTYxtDatuqXRNIAmJT8gMJmbSyOLOP_vnDvbeMUiBsqCRrkTGVbWSwYSc8sTVVE-535kYdqQOgNjH1ffCoZLGl8YYxLnpb2CXJlRQPrcjkA&state=6789

If authorization fails, your app receives a GET request to the specified redirect URL with the error, error_description, and state (if applicable) parameters.

Exchange an authorization code for access tokens

When you have parsed the authorization code, exchange it for tokens to access desired Roblox resources:

  1. Request an access token and refresh token by sending a POST request to the token exchange endpoint. The request must include the authorization code, client ID, and the code verifier value (PKCE) or client secret (non-PKCE) in x-www-form-urlencoded format.

  2. Parse the applicable tokens from the response received. The access token is valid for 15 minutes. Store the refresh token securely, typically on the server side, so you can use it to obtain new tokens in the future.

    {
      "access_token": "eyJhbGciOiJFUzI1NiIsImtpZCI6IlBOeHhpb2JFNE8zbGhQUUlUZG9QQ3FCTE81amh3aXZFS1pHOWhfTGJNOWMiLCJ0eXAiOiJKV11234.eyJzdWIiOiIyMDY3MjQzOTU5IiwiYWlkIjoiM2Q2MWU3NDctM2ExNS00NTE4LWJiNDEtMWU3M2VhNDUyZWIwIiwic2NvcGUiOiJvcGVuaWQ6cmVhZCBwcm9maWxlOnJlYWQiLCJqdGkiOiJBVC5QbmFWVHpJU3k2YkI5TG5QYnZpTCIsIm5iZiI6MTY5MTYzOTY5OCwiZXhwIjoxNjkxNjQwNTk4LCJpYXQiOjE2OTE2Mzk2OTgsImlzcyI6Imh0dHBzOi8vYXBpcy5yb2Jsb3guY29tL29hdXRoLyIsImF1ZCI6IjcyOTA2MTAzOTc5ODc5MzQ5Nj1234.BjwMkC8Q5a_iP1Q5Th8FrS7ntioAollv_zW9mprF1ats9CD2axCvupZydVzYphzQ8TawunnYXp0Xe8k0t8ithg",
      "refresh_token": "eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwia2lkIjoidGpHd1BHaURDWkprZEZkREg1dFZ5emVzRWQyQ0o1NDgtUi1Ya1J1TTBBRSIsInR5cCI6IkpXVCJ9..nKYZvjvXH6msDG8Udluuuw.PwP-_HJIjrgYdY-gMR0Q3cabNwIbmItcMEQHx5r7qStVVa5l4CbrKwJvjY-w9xZ9VFb6P70WmXndNifnio5BPZmivW5QkJgv5_sxLoCwsqB1bmEkz2nFF4ANLzQLCQMvQwgXHPMfCK-lclpVEwnHk4kemrCFOvfuH4qJ1V0Q0j0WjsSU026M67zMaFrrhSKwQh-SzhmXejhKJOjhNfY9hAmeS-LsLLdszAq_JyN7fIvZl1fWDnER_CeDAbQDj5K5ECNOHAQ3RemQ2dADVlc07VEt2KpSqUlHlq3rcaIcNRHCue4GfbCc1lZwQsALbM1aSIzF68klXs1Cj_ZmXxOSOyHxwmbQCHwY7aa16f3VEJzCYa6m0m5U_oHy84iQzsC-_JvBaeFCachrLWmFY818S-nH5fCIORdYgc4s7Fj5HdULnnVwiKeQLKSaYsfneHtqwOc_ux2QYv6Cv6Xn04tkB2TEsuZ7dFwPI-Hw2O30vCzLTcZ-Fl08ER0J0hhq4ep7B641IOnPpMZ1m0gpJJRPbHX_ooqHol9zHZ0gcLKMdYy1wUgsmn_nK_THK3m0RmENXNtepyLw_tSd5vqqIWZ5NFglKSqVnbomEkxneEJRgoFhBGMZiR-3FXMaVryUjq-N.Q_t4NGxTUSMsLVEppkTu0Q6rwt2rKJfFGuvy3s12345",
      "token_type": "Bearer",
      "expires_in": 899,
      "id_token": "eyJhbGciOiJFUzI1NiIsImtpZCI6IkNWWDU1Mi1zeWh4Y1VGdW5vNktScmtReFB1eW15YTRQVllodWdsd3hnNzgiLCJ0eXAiOiJKV11234.eyJzdWIiOiIyMDY3MjQzOTU5IiwibmFtZSI6ImxpbmtzZ29hdCIsIm5pY2tuYW1lIjoibGlua3Nnb2F0IiwicHJlZmVycmVkX3VzZXJuYW1lIjoibGlua3Nnb2F0IiwiY3JlYXRlZF9hdCI6MTYwNzM1NDIzMiwicHJvZmlsZSI6Imh0dHBzOi8vd3d3LnJvYmxveC5jb20vdXNlcnMvMjA2NzI0Mzk1OS9wcm9maWxlIiwibm9uY2UiOiIxMjM0NSIsImp0aSI6IklELnltd3ZjTUdpOVg4azkyNm9qd1I5IiwibmJmIjoxNjkxNjM5Njk4LCJleHAiOjE2OTE2NzU2OTgsImlhdCI6MTY5MTYzOTY5OCwiaXNzIjoiaHR0cHM6Ly9hcGlzLnJvYmxveC5jb20vb2F1dGgvIiwiYXVkIjoiNzI5MDYxMDM5Nzk4NzkzNDk2NCJ9.kZgCMJQGsariwCi8HqsUadUBMM8ZOmf_IPDoWyQY9gVX4Kx3PubDz-Q6MvZ9eU5spNFz0-PEH-G2WSvq2ljDyg",
      "scope": "openid profile"
    }

Make a call to a resource method

Now that you have the required access token, you can use it to make authenticated calls to resource methods. Include the access token in the header of all API requests to authorize them.

For example, you can test if your app functions correctly by going through the entire authorization flow and then making a GET request to the user information endpoint with an access token. Ensure you have the openid or both the openid and profile scopes before calling this endpoint. If successful, the response from that endpoint looks like this:

{
  "sub": "12345678",
  "name": "Jane Doe",
  "nickname": "robloxjanedoe",
  "preferred_username": "robloxjanedoe",
  "created_at": 1607354232,
  "profile": "https://www.roblox.com/users/12345678/profile"
}