We’ve migrated our documentation to a new site, which means some URLs have changed. If you hit a broken link, submit a support ticket.
Subscriptions

Piano OAuth

OAuth support

OAuth-1.png

There are four steps when working with OAuth (click the diagram above for an overview of the flow). For users with an existing valid access token, skip to Step 3. For users with an expired access token, skip to Step 4.

Step 1: Get authorization code

Important: If you want to use the OAuth authorization code flow instead of JWT for OIDC, you must contact Piano Support to manually activate this feature for your account.

Endpoint: Get OAuth authorization code — /id/api/v1/identity/authorize

Redirects the user to a Piano-hosted login/registration page. On successful authentication, the user is sent back to your redirect_uri with the OAuth authorization code as the code query parameter. In addition to email and password, clients using Piano ID can also offer social login and passwordless login.

Important: The redirect_uri must be registered in your Piano ID configuration as an authorized redirect URL.

Parameter

Type

Required

Description

client_id

String

Yes

Your Piano AID.

response_type

String

Yes

Set to the literal value code.

redirect_uri

String

Yes

The callback URL of your application. Must be registered in your Piano ID configuration as an authorized redirect URL.

state

String

No

Recommended for CSRF protection. An opaque value set by your application, returned unchanged in the redirect response, allowing you to verify the response originates from this request.

code_challenge

String

No

PKCE only. Base64URLSafe-encoded SHA-256 hash of the code_verifier. See the PKCE support section.

code_challenge_method

String

No

PKCE only. Set to the literal value S256.

disable_sign_up

Boolean

No

Set to true to disable new user registration — login only. Handled by the Piano ID UI layer.

screen

String

No

Set to register to show the registration screen instead of the default login screen. Handled by the Piano ID UI layer.

Authorization request:

GET https://api.piano.io/id/api/v1/identity/authorize?response_type=code&client_id=<AID>&redirect_uri=<REDIRECT_URI>&state=<STATE>

This URL is opened in a browser — the user authenticates on the Piano ID page, then is redirected back to your application. You can also use it in automated testing via cURL:

curl -v 'https://api.piano.io/id/api/v1/identity/authorize?response_type=code&client_id=<AID>&redirect_uri=<REDIRECT_URI>&state=<STATE>'

On successful authentication, the user is redirected to:

<REDIRECT_URI>?code=<AUTHORIZATION_CODE>&state=<STATE>

Pass the code value to Step 2. Verify that the returned state matches the value you sent.

Step 2: Get access token

Endpoint: Get or refresh access token/id/api/v1/identity/token

Exchanges the OAuth authorization code from Step 1 for an access token. Store the access_token for use in subsequent API requests. Parameters are sent as a form-encoded POST body.

Parameter

Type

Required

Description

client_id

String

Yes

Your Piano AID.

client_secret

String

Yes

OAuth client secret, configured during Piano Identity management setup.

code

String

Yes

The OAuth authorization code received in Step 1.

grant_type

String

Yes

Set to the literal value authorization_code.

redirect_uri

String

Yes

The callback URL of your application. Must match the value sent in Step 1.

Token request:

POST https://api.piano.io/id/api/v1/identity/token
Content-Type: application/x-www-form-urlencoded

client_id=<AID>&client_secret=<OAUTH_CLIENT_SECRET>&redirect_uri=<REDIRECT_URI>&grant_type=authorization_code&code=<AUTHORIZATION_CODE>

cURL example:

curl --location --request POST 'https://sandbox.piano.io/id/api/v1/identity/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=<AID>' \
--data-urlencode 'client_secret=<OAUTH_CLIENT_SECRET>' \
--data-urlencode 'redirect_uri=<REDIRECT_URI>' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode 'code=<AUTHORIZATION_CODE>'

If the request is successful, the response will look like the following JSON:

{
    "access_token": "eyJhbGci...",
    "token_type": "Bearer",
    "refresh_token": "RFbqr...",
    "expires_in": 3600,
    "email_confirmation_required": false
}

The expires_in value is in seconds. The default is 3600 (1 hour) and is configurable in your Piano Identity management configuration.

When using Auth Sync in a mobile app and calling /identity/token/validation from a custom script, you must include the Device-Id header in the request to ensure identity.create events are associated with visitors in Piano Analytics.

On iOS, the device ID can be retrieved using PianoConfiguration.shared.deviceId. On Android, where no public API is currently available, the device ID can be read from SharedPreferences as a workaround. More details are available in the Identity linking documentation.

If your client cannot send application/x-www-form-urlencoded, use /id/api/v1/identity/oauth/token instead — it accepts the same parameters with Content-Type: application/json.

Step 3: Check access

Once you have the access token, check whether the user should be granted access to a resource.

Endpoint: Check resource access/api/v3/access/check

Use your AID and Resource ID to check whether the user has access to a specific resource.

Parameter

Type

Required

Description

rid

String

Yes

Resource ID to check access for.

aid

String

Yes

Your Piano AID.

Authorization

String

Yes

Request header. Pass the access token as Bearer <ACCESS_TOKEN>.

Access check request:

GET https://api.piano.io/api/v3/access/check?rid=<RESOURCE_ID>&aid=<AID>
Authorization: Bearer <ACCESS_TOKEN>

cURL example:

curl 'https://api.piano.io/api/v3/access/check?rid=<RESOURCE_ID>&aid=<AID>' \
--header 'Authorization: Bearer <ACCESS_TOKEN>'

If the request is successful, the response will look like the following JSON:

{
    "code": 0,
    "ts": 1469127375,
    "access": {
        "access_id": null,
        "granted": false,
        "resource": null,
        "user": null,
        "expire_date": 1469127374
    }
}

If the user has access to the resource, granted will be true.

Endpoint: Get user access list/api/v3/user/access/list

Alternatively, get all current access objects for the user. Use this method if multiple resources can grant access, or if you prefer not to hard-code a Resource ID into your application.

Parameter

Type

Required

Description

aid

String

Yes

Your Piano AID.

Authorization

String

Yes

Request header. Pass the access token as Bearer <ACCESS_TOKEN>.

User access list request:

GET https://api.piano.io/api/v3/user/access/list?aid=<AID>
Authorization: Bearer <ACCESS_TOKEN>

cURL example:

curl 'https://api.piano.io/api/v3/user/access/list?aid=<AID>' \
--header 'Authorization: Bearer <ACCESS_TOKEN>'

For more information, see our API documentation: /access/check, /user/access/list, /user/access/get, and /user/get.

Step 4: Refresh access token

Endpoint: Get or refresh access token/id/api/v1/identity/token

Access tokens expire — the duration is configurable in your Piano Identity management configuration. Use the refresh_token from Step 2 to get a new access token without restarting the full OAuth flow. Note that client_secret and redirect_uri are not required for the refresh token flow. Parameters are sent as a form-encoded POST body.

Parameter

Type

Required

Description

client_id

String

Yes

Your Piano AID.

grant_type

String

Yes

Set to the literal value refresh_token.

refresh_token

String

Yes

The refresh token received in Step 2.

Token refresh request:

POST https://api.piano.io/id/api/v1/identity/token
Content-Type: application/x-www-form-urlencoded

client_id=<AID>&grant_type=refresh_token&refresh_token=<REFRESH_TOKEN>

cURL example:

curl --location --request POST 'https://api.piano.io/id/api/v1/identity/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=<AID>' \
--data-urlencode 'grant_type=refresh_token' \
--data-urlencode 'refresh_token=<REFRESH_TOKEN>'

If the request is successful, the response will look like the following JSON:

{
    "access_token": "eyJhbGci...",
    "token_type": "Bearer",
    "refresh_token": "RFbqr...",
    "expires_in": 3600,
    "email_confirmation_required": false
}

By default, a new refresh_token is issued with each refresh response. Always use the latest refresh_token for subsequent refresh requests. If your application is configured to use a fixed refresh token, the same value is reused. To change this behavior, contact Piano support.

Refresh token lifetime. A refresh token expires access token lifetime + 30 days from the moment it was issued. Each successful refresh resets the clock — the new refresh token again expires access token lifetime + 30 days from that moment. This means a user's session continues indefinitely as long as the app performs at least one refresh within each window. A user is only logged out if no refresh occurs for the entire duration of a refresh token window.

Example: with the default access token lifetime of 1 hour, each refresh token lasts approximately 30 days (1 h + 720 h). After a successful refresh, the new refresh token expires 30 days from that moment, not from the original login.

In addition to refreshing the access token, periodically recheck whether the user should still have access. This ensures access is revoked for users whose subscriptions have lapsed. To recheck access, repeat Step 3.

PKCE support

Piano's OAuth integration supports the PKCE extension.

The Proof Key for Code Exchange (PKCE) extension protects public clients against authorization code interception. The client generates a random secret (code_verifier) and includes its hash (code_challenge) in the PKCE Step 1 authorization request. When exchanging the authorization code for a token in PKCE Step 2, the original code_verifier is provided. If the authorization code is intercepted, it cannot be used without the matching code_verifier.

Step 1: Get authorization code

Endpoint: Get OAuth authorization code — /id/api/v1/identity/authorize

The same endpoint as Step 1 in the OAuth support section above, with code_challenge and code_challenge_method added as required parameters.

Parameter

Type

Required

Description

client_id

String

Yes

Your Piano AID.

response_type

String

Yes

Set to the literal value code.

redirect_uri

String

Yes

The callback URL of your application, registered in your Piano ID configuration.

code_challenge

String

Yes

Base64URLSafe-encoded SHA-256 hash of the code_verifier. See the example below.

code_challenge_method

String

Yes

Set to the literal value S256.

state

String

No

Recommended for CSRF protection. Returned unchanged in the redirect response.

Authorization request:

GET https://api.piano.io/id/api/v1/identity/authorize?response_type=code&client_id=<AID>&redirect_uri=<REDIRECT_URI>&code_challenge=<CODE_CHALLENGE>&code_challenge_method=S256&state=<STATE>

cURL example:

curl -v 'https://api.piano.io/id/api/v1/identity/authorize?response_type=code&client_id=<AID>&redirect_uri=<REDIRECT_URI>&code_challenge=<CODE_CHALLENGE>&code_challenge_method=S256&state=<STATE>'

On success, you are redirected to a URL containing the OAuth authorization code:

<REDIRECT_URI>?code=<AUTHORIZATION_CODE>&state=<STATE>

The code_challenge is the Base64URLSafe-encoded SHA-256 hash of the code_verifier. Example in Java can be found below.

Generating code_verifier and code_challenge:

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.nio.charset.StandardCharsets;
import org.apache.commons.codec.binary.Base64;

public static void main(String[] args) throws NoSuchAlgorithmException {
    byte[] randomBytes = new byte[32];
    new SecureRandom().nextBytes(randomBytes);

    String codeVerifier = Base64.encodeBase64URLSafeString(randomBytes);

    MessageDigest hash = MessageDigest.getInstance("SHA-256");
    hash.update(codeVerifier.getBytes(StandardCharsets.US_ASCII));
    String codeChallenge = Base64.encodeBase64URLSafeString(hash.digest());

    System.out.println("code_verifier:  " + codeVerifier);
    System.out.println("code_challenge: " + codeChallenge);
}

Step 2: Get access token

Endpoint: Get or refresh access token/id/api/v1/identity/token

Exchanges the authorization code from Step 1 for a Piano access token. Use code_verifier in place of client_secret — not both. Parameters are sent as a form-encoded POST body.

Parameter

Type

Required

Description

client_id

String

Yes

Your Piano AID.

grant_type

String

Yes

Set to the literal value authorization_code.

code

String

Yes

The OAuth authorization code received in Step 1.

redirect_uri

String

Yes

Same value used in Step 1.

code_verifier

String

Yes

The plain-text secret generated in Step 1.

Token request:

POST https://api.piano.io/id/api/v1/identity/token
Content-Type: application/x-www-form-urlencoded

client_id=<AID>&redirect_uri=<REDIRECT_URI>&grant_type=authorization_code&code=<AUTHORIZATION_CODE>&code_verifier=<CODE_VERIFIER>

cURL example:

curl --location --request POST 'https://api.piano.io/id/api/v1/identity/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=<AID>' \
--data-urlencode 'redirect_uri=<REDIRECT_URI>' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode 'code=<AUTHORIZATION_CODE>' \
--data-urlencode 'code_verifier=<CODE_VERIFIER>'

Response:

{
    "access_token": "eyJhbGci...",
    "token_type": "Bearer",
    "refresh_token": "RFbqr...",
    "expires_in": 3600,
    "email_confirmation_required": false
}

If your client cannot send application/x-www-form-urlencoded, use /id/api/v1/identity/oauth/token instead — it accepts the same parameters with Content-Type: application/json.

Step 3: Refresh access token

Endpoint: Get or refresh access token/id/api/v1/identity/token

The refresh flow for PKCE uses the same endpoint, parameters, and cURL example as Step 4 in the OAuth support section above. No code_verifier is required for refresh.

For more information on the general OAuth flow, see the OAuth support section above. All available base URLs are listed in the Piano ID API reference.

Last updated: