/token
The /token
endpoint supports the following grants:
-
This is the last step of the authentication and authorization flow. A client may use this grant to trade the
code
it received at itsredirect_uri
for an access token and, at the discretion of the server, a refresh token after if verifies thestate
parameter. -
If a client possesses a refresh token, it may use this grant to trade the refresh token for a new access token and at the discretion of the server a new refresh token.
-
If a client has been issued a secret and up to three users have been added to the access list, the client may use this grant to perform user authentication directly for an access token and at the discretion of the server a refresh token.
The format the of response is the same for all types of grant.
Authorization Code Grant
If the client receives a successful response from
/authorize at its redirect_uri
, it should verify the
state
parameter and if the state is valid the client may trade the code
it
received for an access token.
This is an authorization_code
grant:
POST /oauth2/token HTTP/1.1
Host: oauth.iracing.com
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&client_id=as_issued&client_secret=if_issued&code=3122eb8a-7a91-4dae-b174-07df2b083913&redirect_uri=https%3A%2F%2Fregistered-app.com%2Fcallback&code_verifier=...elided...
The server expects the form data to be URL (percent) encoded. If the client
library does not do this automatically, it will have to be done manually.
This is very important for both the client_secret
and redirect_uri
parameters.
Parameters
-
grant_type
(required)The value is "authorization_code" to trade in a
code
for an access token. -
client_id
(required)The client identifier issued during client registration.
-
client_secret
(required only if issued)The client secret is only issued to some client types during client registration.
The client secret must be masked before it is sent to the server.
-
code
(required)As returned to the
redirect_uri
of the client. -
redirect_uri
(required)The same
redirect_uri
used to /authorize. -
code_verifier
(required for code challenge)The PKCE code verifier which is only required if a
code_challenge
was used to /authorize.
Refresh Token Grant
If the client possesses a refresh token, it may trade the refresh token for new access and refresh tokens. A refresh token may only be used once.
This is a refresh_token
grant:
POST /oauth2/token HTTP/1.1
Host: oauth.iracing.com
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token&client_id=as_issued&client_secret=if_issued&refresh_token=...elided...
The server expects the form data to be URL (percent) encoded. If the client
library does not do this automatically, it will have to be done manually.
This is very important for the client_secret
parameter.
Parameters
-
grant_type
(required)The value is "refresh_token" to trade in a refresh token for new access and refresh tokens.
-
client_id
(required)The client identifier issued during client registration.
-
client_secret
(required only if issued)The client secret is only issued to some client types during client registration.
-
refresh_token
(required)As returned in the response.
Password Limited Grant
Some clients may not allow users to login directly, but instead run on a server in a headless mode. These clients import (scrape) data available to all authenticated users via the Data API Workflow. One option for these clients is to complete the usual web-based authentication manually and store the resulting access and refresh tokens for the client to use. While this can be made to work, any need to reauthenticate will require the same manual intervention.
To provide a more automated option, this server supports the Password Limited Grant. This grant is similar to the "password" grant, formally known as the Resource Owner Password Credentials Grant. The "password" grant was later deprecated for OAuth 2.0 and removed completely from OAuth 2.1 due to security concerns.
The Password Limited Grant is an in-house extension of OAuth 2.1 intended for use by clients which run unattended on behalf of a user who may be registered registered by contacting us. Only the registered user will be able to make use of this grant for the client.
Limiting this grant to the registered user alleviates some of the security concern around resource owner exposure to the client. It is expected that the client will be handling the passwords of the client developer only. The responsibility for protecting these credentials falls on the client developer.
This does not encourage users to enter their credentials in places other than the authorization server. Only the registered user will be able to use this grant with the client. The general public will not be able to use this grant with the client.
The security concerns around two-factor authentication and authentication with cryptographic credentials hold, however. Even if configured for the user, two-factor authentication (2FA) will not be enforced when using this grant. Similarly, should this server start supporting WebCrypto or WebAuthn in the future, it is expected that this grant will not allow using those credentials.
Expect calls to this grant to take two seconds or more to complete. This grant is intended to be used once at client start-up. The Refresh Token Grant should be used to maintain the access token instead of re-issuing a call to this grant. Strict rate limiting and lockouts on failure are employed to ensure against abuse. Violations of rate limits will result in the client becoming temporarily unauthorized.
Rate limits are communicated in the response headers:
RateLimit-Limit
: the number of requests allowed in the current time windowRateLimit-Remaining
: the number of requests remaining in the current time windowRateLimit-Reset
: the number of seconds until the end of the current time window
If the client exceeds the rate limit, the server will respond with a
400 Bad Request
status code, an
unauthorized_client error, and a Retry-After
header indicating the number of seconds to wait before making another request.
This is a password_limited
grant:
POST /oauth2/token HTTP/1.1
Host: oauth.iracing.com
Content-Type: application/x-www-form-urlencoded
grant_type=password_limited&client_id=as_issued&client_secret=...elided...&username=john.west%40iracing.com&password=...elided...&scope=iracing.auth
The server expects the form data to be URL (percent) encoded. If the client
library does not do this automatically, it will have to be done manually.
This is very important for the client_secret
, username
, password
and
scope
parameters.
Parameters
-
grant_type
(required)The value is "password_limited" to use credentials to obtain an access token.
-
client_id
(required)The client identifier issued during client registration.
-
client_secret
(required)The client secret is only issued to some client types during client registration.
The client secret must be masked before it is sent to the server.
-
username
(required)The email address or other issued identifier for a user.
-
password
(required)The password of the user.
The password must be masked before it is sent to the server.
-
scope
(optional)One or more scopes to request, if any, separated by white space.
Response
This endpoint will respond with an application/json
object:
{
"access_token": "...elided...",
"token_type": "Bearer",
"expires_in": 600,
"refresh_token": "...elided...",
"refresh_token_expires_in": 3600,
"scope": "iracing.auth"
}
The fields are:
-
access_token
The access token may be used to authorize a connection to a resource server. The value is considered opaque and its format may change without warning at our discretion.
-
token_type
This will always be the string "Bearer".
-
expires_in
The number of seconds after which this access token will no longer be considered valid.
-
refresh_token
(may be omitted)The refresh token may be used in a Refresh Token Grant to obtain new access and refresh tokens. Each refresh token may only be used once. The value is considered opaque and its format may change without warning at our discretion.
The server may not issue a refresh token, in which case this field will be omitted.
-
refresh_token_expires_in
(may be omitted)The number of seconds after which this refresh token will no longer be considered valid.
The server may not issue a refresh token, in which case this field will be omitted.
-
scope
(may be omitted)One or more scopes granted to the access token, if any, separated by white space.
The server may not grant any scopes, in which case this field will be omitted.
Client Secret and User Password Masking
This API expects that any client secret or user password has been masked before it is transmitted as a security precaution against logging plain-text passwords.
To be clear: this masked secret is not what the server stores. The masked secret is hashed again before it is stored.
The masking algorithm is:
use base64::prelude::*; use sha2::{Digest, Sha256}; /// Mask the given secret with the identifier. If masking a client secret, /// the `id` parameter is the client_id. If masking a user's password, the /// `id` parameter is the username (email address) of the user. fn mask(secret: &str, id: &str) -> String { // The hashing algorithm is SHA-256, using the standard alphabet and // padding. // // Other variants, including the URL-safe variant, are incompatible. let mut hasher = Sha256::new(); // The id should be trimmed of leading and trailing white space and lower- // cased. let normalized_id = id.trim().to_lowercase(); // The normalized id is concatenated onto the secret with no separator. hasher.update(format!("{secret}{normalized_id}")); // The SHA-256 hash is base64 encoded and returned. This should be a // string with the following properties: // // - its length can be divided evenly by 4 // - may end with '=', '==', or no equal signs at all // - all the other characters are in the class [A-Za-z0-9+/] BASE64_STANDARD.encode(hasher.finalize()) } fn main() { let secret = "Anagram-tactics-FOOTING-OPACITY-SHONE-keenly"; let id = " John.West@iracing.com "; let expected = "KIhAi2ynNPWvJsebdluGaBaPTRaUACqTPDCfyUuv46Y="; let masked = mask(secret, id); if masked == expected { println!("Got it! This secret is masked."); } else { println!("Oh, no! Something went wrong."); println!("masked: {masked}"); println!("expect: {expected}"); } // If, and only if, the web client does not automatically URL encode web // form data the masked secret will have to be URL encoded: let expected = "KIhAi2ynNPWvJsebdluGaBaPTRaUACqTPDCfyUuv46Y%3D"; let url_encoded: String = form_urlencoded::byte_serialize(masked.as_bytes()).collect(); if url_encoded == expected { println!("Got it! This masked secret is URL encoded."); } else { println!("Oh, no! Something went wrong."); println!("masked: {url_encoded}"); println!("masked: {masked}"); } }
On other languages and platforms:
import crypto from "node:crypto";
function mask(secret, id) {
const hasher = crypto.createHash("sha256");
const normalized_id = id.trim().toLowerCase();
hasher.update(`${secret}${normalized_id}`);
return hasher.digest("base64");
}
async function mask(secret, id) {
const normalized_id = id.trim().toLowerCase();
const encoder = new TextEncoder();
const encoded = encoder.encode(`${secret}${normalized_id}`);
const digest = await crypto.subtle.digest("SHA-256", encoded);
return btoa(String.fromCharCode(...new Uint8Array(digest)));
}