Introduction
iRacing.com provides an OAuth 2.0 compatible service for authentication and authorization.
For more information on OAuth 2.0, the documentation at oauth.com is an excellent resource.
This guide covers what is required to leverage the iRacing.com Authorization Service in an application, and additional features beyond the OAuth 2.0 specification.
Notice
iRacing does not provide explicit authorization to 3rd parties who develop software that works alongside the simulation or website(s). Usage of an OAuth client, the iRacing SDK, the iRacing /data API, and the data they provide is governed by the terms members agreed to during sign-up and detailed in the following documents:
iRacing may modify these documents and their terms at any time and may restrict or remove access to various data at its discretion and without notice.
Reading this Book
This chapter introduces the various features and assumptions of this book. It assumes you are reading an HTML version of this book, live on the server. The available options and formatting will differ for other formats, such as the print version or PDF.
This book is organized into chapters, each on a separate page. Chapters may be nested into a hierarchy, and each chapter is subdivided by headings.
Navigation
On the left is a sidebar which provides a list of all chapters. Click on the chapter title to move to a different page. If the sidebar is not present, it may be opened using the icon.
The and buttons at the sides of the page may be used to go to the Previous and Next page, respectively. The left and right arrow keys will also do the same.
Tools
At the top of the page is the title bar which provides several tools.
| Icon | Description |
|---|---|
| Open and close the sidebar | |
| Choose a theme | |
| Search the book | |
| Print the book |
Clicking the title bar will return you to the top of the page.
Search
A search box may be opened by pressing the search icon
() or by pressing the S key on the
keyboard. Click the results, or use the up and down arrow keys and enter to open
the highlighted result, to navigate to that section.
The search term will be highlighted in the page. Clicking on the highlighted
search term or using the Esc key will clear the highlighting.
Code Blocks
Code blocks may contain several different icons for interacting with them:
| Icon | Description |
|---|---|
| Undo changes (Rust only) | |
| Copy the code into your clipboard | |
| Execute the code (Rust only) |
The icons appear when hovering over the code block.
Examples
Examples are provided to demonstrate how to call API endpoints and the expected responses.
Requests
Example requests are written in the HTTP wire format and give important information about how to invoke the endpoints.
HTTP GET Request
A typical HTTP GET request looks like:
GET https://oauth.iracing.com/oauth2/some/endpoint?example=true&another=false HTTP/1.1
Authorization: Bearer ...elided...
Examining each piece in turn:
-
GETThis is an HTTP
GETrequest, as opposed toPOSTor some other HTTP method. This is what a web browser does when naviating to a page. Parameters to the endpoint will appear at the end of the endpoint URL. -
https://oauth.iracing.com/oauth2/some/endpoint?example=true&another=falseThis is the endpoint URL. It starts with the Base URL and continues with the API endpoint and any parameters. In this case, the endpoint
/some/endpointfollowed by the parametersexamplewith the valuetrueandanotherwith the valuefalse. -
HTTP/1.1This is an implementation detail of the HTTP protocol, specifying its version. Most clients will allow for this to be omitted.
-
Authorization: Bearer ...elided...Some requests will require an access token. If an access token is required, the example will have this text. This is an
Authorizationheader with an authentication scheme of ‘Bearer’. The full text of the access token should be used in place of...elided...here.
HTTP POST Request
A typical HTTP POST request looks like:
POST /oauth2/other/endpoint HTTP/1.1
Host: oauth.iracing.com
Authorization: Bearer ...elided...
Content-Type: application/x-www-form-urlencoded
example=true&another=false
Examining each piece in turn:
-
POSTThis is an HTTP
POSTrequest, as opposed toGETor some other HTTP method. Browsers typically do this when submitting a form, for example. Parameters to the endpoint are sent in the body of the request. -
/oauth2/other/endpointThis is the endpoint URL, which starts with the non-host part of the Base URL and continues with the API endpoint. In this case, this is
/other/endpointas it would be referenced in this book. -
HTTP/1.1This is an implementation detail of the HTTP protocol, specifying its version. Most clients will allow for this to be omitted.
-
Host: oauth.iracing.comThis is a header that designates the server which should handle the request. Most clients will take care of this automatically.
-
Authorization: Bearer ...elided...Some requests will require an access token. If an access token is required, the example will have this text. This is an
Authorizationheader with an authentication scheme of ‘Bearer’. The full text of the access token should be used in place of...elided...here. -
Content-Type: application/x-www-form-urlencodedA
Content-Typeheader tells the developer what format the API is expecting for any data submitted in the POST body. The API uses theapplication/x-www-form-urlencodedfor the POST body format whenever possible. -
example=true&another=falseA blank line separates the last header from the POST body data. In this case, the data are the parameters
examplewith the valuetrueandanotherwith the valuefalse.
Responses
Responses are often simplified and not written in the full HTTP wire format.
JSON Responses
This API favors JSON response bodies whenever appropriate. The documentation
will call these out as an application/json object and give an example of the
structure with sample data:
{
"example": true,
"another": false
}
An HTTP status code of 200 can be assumed.
Status-Code Only Responses
When there is no relevant data to be returned in the body, this will be noted in the documentation along with the expected status code:
“It responds with no body. An HTTP status code of 200 indicates success.”
HTTP Wire Format Responses
Occasionally, the documentation may call out the full HTTP wire format of a response:
HTTP/1.1 307 Temporary Redirect
Location: https://localhost/some/endpoint?example=true&another=false
Examining each piece in turn:
-
HTTP/1.1This is an implementation detail of the HTTP protocol, specifying its version.
-
307The HTTP status code.
-
Temporary RedirectThe canonical name of the HTTP status code.
-
Location:This is a Location header, instructing the browser to navigate to a new URL.
-
https://localhost/some/endpoint?example=true&another=falseThis is the URL to which the browser should navigate. It starts with the URL and continues with any parameters. In this case, the endpoint
https://localhost/some/endpointfollowed by the parametersexamplewith the valuetrueandanotherwith the valuefalse.
Error Responses
Error responses may be in any of the above formats. See the Errors chapter for more information.
Client Registration
Important
We have paused the creation of OAuth client IDs while we evaluate existing 3rd party usage of iRacing’s APIs and SDK. When creation of OAuth client IDs is enabled again, we will post on our forums and include a mention in the release notes.
Workflows
Many clients of this service will follow a similar workflow to achieve their goals. This section describes common workflows which may be used as models.
- The Identity Verification Workflow may be used by clients to get the iRacing customer identifier of the authenticated user. Previously, clients might have used private messages on the iRacing forums or other means to accomplish this goal.
- The Data API Workflow may be used by clients interested in making queries to /data on the behalf of an authenticated iRacing member. Previously, clients might have been constrained to using only the parts of the Data API which are not customer-specific. This workflow allows the client to access the full Data API without the need to handle user passwords.
Multiple workflows may be used by the same client as necessary. A client developer may prefer to use the Identity Verification Workflow even though the Data API Workflow gives access to a superset of the /iracing/profile information by way of /data/member/info.
Users may prefer clients which request the fewest permissions.
Identity Verification Workflow
This workflow may be used by clients to get the iRacing customer identifier of the authenticated user. Previously, clients might have used private messages on the iRacing forums or other means to accomplish this goal.
Goals
- Obtain the iRacing customer identifier or display name for a user.
Prerequisites and Assumptions
- The client has been registered.
- No refresh tokens will be issued and the access token will be used only once so there is no need to store it.
- The user has created an account within the client and has authenticated within the client.
Steps
- The client starts an Authentication and Authorization
flow by invoking /authorize. The client should
request the scope
iracing.profileto get access to the iRacing profile and specify the correctredirect_uriregistered to the client. - The user is asked to login and is told that the client has requested access
to their profile information. It is assumed the user will approve. The
browser is redirected to the
redirect_uriwith acode. - The client receives the
codeat itsredirect_uri. It may then use /token to trade thecodefor tokens. - Using the access token, the client makes a request to /iracing/profile. The profile information is returned as a JSON object in the response, including the customer identifier and display name.
- The client discards the access token.
Data API Workflow
This workflow may be used by clients interested in making queries to /data on the behalf of an authenticated iRacing member. Previously, clients might have been constrained to using only the parts of the Data API which are not customer-specific. This workflow allows the client to access the full Data API without the need to handle user passwords.
Goals
- Use the full Data API without handling user passwords.
Prerequisites and Assumptions
- The client has been registered with an audience of
data-server. - The client can store tokens securely.
Steps
- The client checks to see if it has an access token which
has not expired. If so, it may use the access token as a
Bearertoken in theAuthorizationheader while making requests to the Data API. It may continue to make requests until the access token expires or is otherwise invalidated. - The client checks to see if it has a refresh token which has not expired. If so, it may use the refresh token with the /token endpoint to obtain new tokens. The client securely stores the new tokens. Return to Step 1.
- If the client has no valid access or refresh token, the client starts an
Authentication and Authorization flow by invoking
/authorize. The client should request the
scope
iracing.authto allow making requests as the authenticated user and specify the correctredirect_uriregistered to the client. - The user is asked to login and is told that the client has requested access
to iRacing services. It is assumed the user will approve. The browser is
redirected to the
redirect_uriwith acode. - The client receives the
codeat itsredirect_uri. It may then use /token to trade thecodefor new tokens. The client securely stores the new tokens. Return to Step 1.
Authentication Flows
This service supports multiple ways to authenticate:
- The Authorization Code Flow is used by clients which are interactive. These clients work with a web browser to guide the user through Authentication and Authorization. This is the preferred flow and should be used whenever possible.
- The Password Limited Flow is used by clients which are not interactive. These clients run on servers and retrieve data using the Data API Workflow on behalf of a limited number of users.
Authorization Code Flow
This is the preferred Authentication Flow. When using this flow, the client will work with the user’s web browser to guide them through authenticating with this service.
The full process is detailed in the Authentication and Authorization section of the book. It entails directing the user’s browser to /authorize, waiting for a response at a pre-registered Redirect URI for an authorization code, and finally requesting an Authorization Code Grant from /token to obtain tokens.
This authentication flow should be used if possible. It MUST be used if:
- the client code is distributed to end-users; a Single-Page Web Application, a Mobile Application, or a Native Application
- the client cannot keep its secret confidential
- the client serves a broad user-base; i.e., every iRacing user is a potential user of the client
Password Limited Flow
This authentication flow is used by clients which have no interactive user interface (a headless client). When making requests on behalf of other users, the Authorization Code Flow must be used instead.
This is an in-house extension of OAuth 2.1 which allows for logging in directly with user credentials. This is antithetical to the preferred authentication flow which allows this service to maintain strict custody of user credentials. It is based on an OAuth 2.0 specification called the Resource Owner Password Credentials Grant which was later deprecated in OAuth 2.0 and removed from OAuth 2.1 due to security concerns.
This service adds limits to this flow which mitigate these concerns:
- the client MUST allow for fewer than three users,
- users of the client MUST be pre-registered by contacting us; and
- the client MUST be able to keep its client secret confidential.
This flow does bypass any two-factor authentication (2FA) which might be configured for the user. If this service starts supporting WebCrypto or WebAuthn in the future, this flow will not support using those credentials.
To use this flow the client requests a Password Limited Grant from /token. The access and refresh tokens will be returned in the response.
It falls on the client developer to secure not only the access and refresh tokens, but the client secret and user credentials as well.
See the documentation for the Password Limited Grant for other restrictions and considerations.
Sample Workflow
- Authenticate by calling /token with
grant_type=password_limited.- The response will be an access token (600 second lifetime / repeated use) and refresh token (7 day lifetime/single use). Times given are defaults.
- Make requests with the access token for ~590 seconds (providing some buffer).
- Allowing it to expire is fine if you want to just make requests until the endpoint tells you the token is expired.
- Call /token with
grant_type=refresh_token- The response is a new access token (600 second lifetime / repeated use) and refresh token (7 day lifetime/single use). Times given are defaults.
- Repeat as necessary
Client Types
Clients fall into either the Public or Confidential categories:
-
Public
The client cannot maintain the confidentiality of a client secret. We WILL NOT issue a client secret to a Public client.
-
Confidential
The client runs on a server where the source code or configuration is not available to the public, allowing it to maintain the confidentiality of a client secret. We MAY issue a client secret to a Confidential client.
Public clients are identified when the user’s browser is redirected to a redirect URI which has been registered to the client.
Confidential clients may be identified by redirect URI if one has been registered. Confidential clients MUST provide the client secret to /token if one has been issued.
Client Roles
Each client may perform one or more roles:
-
End-User Support
This client allows for end-users (iRacing customers) to login, providing Identity Verification or access to user information from the Data API.
There may be hundreds or thousands of on-going sessions for end-users.
-
Internal Support
This client retrieves information from the Data API which is available to any authenticated user; schedules, official session results, and the like.
There might be just one session to retrieve data for the client.
We recommend that these two roles are separated. This allows for better tracking and control. Please contact us we will issue an additional client_id.
Redirect URIs
A Redirect URI is a place on the client to which the server can redirect the user’s browser after they have completed authentication. A client may have zero or more Redirect URIs associated with it. A client using the Authorization Code Flow will have at least one Redirect URI.
All Redirect URIs for a client must be registered with the server before they can be used. Contact us to have new Redirect URIs added or old Redirect URIs removed.
There are rules for which Redirect URIs we will accept. Rules for specific client types or situations override general rules.
A typical Redirect URI might look like:
https://app.yourdomain.example/callback
General Rules
- Redirect URIs must match exactly when used as an argument. If the Redirect URI is registered with query parameters, those query parameters must match byte-for-byte when used as an argument. Similarly, if a port is specified it must match exactly.
- A well-formed Redirect URI will have at least 3 slashes; “https://app.yourdomain.example/” is valid but “https://app.yourdomain.example” is not.
- The “https://” scheme is strongly preferred over “http://”; additional justification may be required before an “http://” Redirect URI is configured.
- Custom URL schemes are supported, such as “my_iracing_app://”, to support mobile applications. We strongly prefer reversed domain names. If the application is associated with the domain “app.yourdomain.com” please use the scheme “example.yourdomain.app://” to avoid conflict.
- Host names are strongly preferred over IP addresses. The exception for this is “localhost” where either “127.0.0.1” or “[::1]” is strongly preferred.
- Port numbers may be specified. The Redirect URI “http://127.0.0.1:12345/callback” is valid.
- Implicit ports may not used unless the Redirect URI is registered with an explicit port number. If the Redirect URI “https://app.yourdomain.example/callback” is registered, using “https://app.yourdomain.example:443/callback” as an argument will fail.
- Any arbitrary path may be used. We prefer paths such as “/”, “/callback”, “/redirect”, or “/login”.
- Parameters are allowed, but the parameters must match exactly. To use Redirect URIs such as “https://app.yourdomain.example/callback?flow=one” and “https://app.yourdomain.example/callback?flow=two” both must be registered.
- There must be no fragment in the URL; “https://app.yourdomain.example/callback#fragment” is not allowed.
Native Application Support
To support native applications we allow Redirect URIs may also be formed like:
http://127.0.0.1:0/callback
- The scheme MUST be “http://”
- The host MUST be the IPv4 (127.0.0.1) or IPv6 ([::1]) loopback address; “localhost” or equivalents will not be accepted.
- The port MUST be 0.
- The other General Rules still hold.
A native application may:
- start a web server bound to an ephemeral port on the loopback device;
- wait for a callback on the web server in the background; then
- start a web browser, or have the user start a browser, submitting a request
to /authorize with a
redirect_urithat substitutes0with the ephemeral port.
The redirect_uri that the client uses for /authorize
might be something like “http://127.0.0.1:25417/callback” assuming the local web
server is listening on port 25417.
When the user completes the Authentication and Authorization
process the user’s web browser will be redirected back to the web server bound
on the ephemeral port. The code and state values may be collected. The web
server can be shut down, and the client may invoke /token
to finish the process.
Client Inactivity
For security, clients that have not been used for an extended period will be disabled. This helps to prevent unauthorized access from forgotten or abandoned clients.
A warning email will be sent after the client has been unused for 90 days, 120 days, and 150 days. If the client remains unused for 180 days, it will be disabled and a final notification email will be sent. Disabled clients cannot be used to obtain new tokens.
Notifications will be sent to the client’s owner, or to the Developer's Email
address provided during client registration if no
owner is registered.
Clients that remain disabled for an extended period may be permanently deleted at our discretion.
To reactivate a disabled client or to request that we disable a client you own, please contact us.
Base URL
Throughout this book API endpoints are written relative to the following URL:
https://oauth.iracing.com/oauth2
x-request-id
Each incoming request is assigned a request identifier, available to clients as
an x-request-id header in the response.
The request identifier is used to associate log messages with the request.
Please include the value of the received x-request-id header when reporting
issues.
Scopes
The following scopes may be requested:
| scope | description |
|---|---|
| iracing.auth | Authorization for iRacing services. |
| iracing.profile | Access to the iRacing profile. |
The iRacing.com Auth Service may not honor all requested scopes. At its discretion, other scopes may be included.
Tokens
Clients receive tokens from the authorization server and present them to resource servers to access protected resources. Tokens are created in response to a grant request made to the /token endpoint.
There are two types of tokens, access tokens and refresh tokens. An access token is the basic unit of identity and permission delegation. They are typically short-lived and are presented to resource servers by clients. A refresh token is a long-lived, single-use token which can be traded with the authorization server for a new access token and refresh token.
Most clients do not need to understand the contents of tokens and should treat them as opaque values.
Tip
In most cases, clients should call /iracing/profile to get information about the user.
Access Token
An access token is used by a client to access protected resources. It is issued by one of the various grants of /token, which is the last step of the Authentication and Authorization process.
An access token is short-lived, typically valid for 10 minutes. It may be used as required until it expires. After expiration, the client must obtain a new access token. If the client has a refresh token, it can use that to obtain a new access token without requiring the user to re-authenticate. This is done through the Refresh Token Grant of the /token endpoint. The typical lifetime of a refresh token is 7 days.
Clients must not rely on the lifetime values above. These lifetimes are the
defaults and may be changed on a per-client basis and at our discretion. The
/token response includes the expires_in and
refresh_token_expires_in fields, the lifetimes of the access and refresh
tokens in seconds. Clients should use these fields at runtime to determine when
to request new tokens.
Advanced Use
Caution
The information in this section is intended for advanced use-cases. Manual token parsing is complex, error-prone, rarely necessary, and can introduce security vulnerabilities in the relying code.
Most clients do not need to understand the contents of access tokens and should treat them as opaque values.
Server-controlled validation and inspection services are provided by the API, such as /iracing/profile. These significantly reduce the complexity of extracting information from an access token and often provide additional information not otherwise available.
Clients with use-cases which require manual access token parsing should carefully read the information in this section and ensure that they understand the security implications of their implementation choices. It is recommended that a well-maintained and widely used JOSE library is used to perform access token validation and verification. If a client chooses to implement access token validation and verification without a library, it should ensure that it performs all of the necessary steps described in this section and in any relevant RFCs to avoid security vulnerabilities in the relying code.
Clients relying on manual token parsing may introduce security vulnerabilities into their own client code. These vulnerabilities are the responsibility of the client, and do not constitute a vulnerability in the authorization server or iRacing.
When in doubt, do not proceed further.
An access token must be validated and verified before its decoded contents can be trusted. It is STRONGLY RECOMMENDED that a trustworthy JOSE library is used to perform these steps. The library should be well-maintained and widely used, and should be kept up to date to ensure that any security vulnerabilities are patched. The library should also be configured to perform most of the necessary validation and verification steps described below. Failure to properly validate and verify access tokens can lead to security vulnerabilities, such as accepting tokens that are expired, not properly signed, or issued for a different audience.
The validation and verification steps are often combined in a single process. Access token validation ensures that its format is correct. Access token verification ensures that the token was issued by the authorization server and has not been modified.
Once all of the validation and verification steps have been completed, the contents of the access token can be trusted and used to make authorization decisions.
Format
An access token is a JSON Web Token (JWT), which has been signed by the authorization server as a JSON Web Signature (JWS). It is encoded in compact serialization format and conforms to the specification in RFC 7515.
Warning
Access tokens MUST NOT be used until they have gone through the validation and verification process. The contents of an access token cannot be trusted until it has been validated and verified. This is a common attack vector against JWT implementations that fail to properly validate and verify tokens before trusting their contents.
Header
The header contains at least the following parameters:
{
"kid": "d760990f-62c9-4570-ac99-9e7477363e18",
"alg": "EdDSA",
"jku": "https://oauth.iracing.com/.well-known/jwks.json"
}
The fields are:
-
kidA string identifier of the key used to sign the token. This is used to identify the correct key in the JSON Web Key Set (JWKS) at the
jkuURL. In this example it is a UUID, but it can be any string. -
algThe algorithm used to sign the token. This is typically “EdDSA”, which indicates that the token is signed using Edwards-curve Digital Signature Algorithm (EdDSA) with the Ed25519 curve. This is a modern signature algorithm that provides strong security and performance. Other algorithms may be used in future implementations, but “none” will never be used.
Warning
The
algclaim will never be “none”. This algorithm indicates that the token is not signed and MUST be rejected. -
jkuThe URL of the JSON Web Key Set (JWKS) containing the public keys used to verify the token’s signature. This is typically https://oauth.iracing.com/.well-known/jwks.json. It MUST use HTTPS, the client MUST verify the TLS certificate, and the URL MUST NOT contain user information, a query component, or a fragment component. Further, the domain component of the URL MUST end with the exact text
.iracing.com.
Payload
The payload contains the claims of the token, which are the statements about an entity (typically, the user), the authorizations the entity has delegated to the client, and additional metadata.
Warning
The payload contains an agreement between the authorization server and the resource servers. This is not an Identity Token, and the claims in the payload are not guaranteed to be present or to keep any documented meaning. The claims in the payload are intended to be used by resource servers to make authorization decisions. Clients should not rely on the presence or meaning of any particular claim in the payload and should treat the access token as an opaque value. For user identity information, use dedicated endpoints such as /iracing/profile instead.
The claims in the payload may change without notice to clients, and new claims may be added at any time.
The payload contains at least the following claims:
{
"session_id": "51687ec8-34d5-4523-ae26-e9f0462b6e44",
"iss": "https://oauth.iracing.com",
"exp": 1680654339,
"aud": ["example_client", "oauth-api"],
"sub": "e8627b85-b178-499d-bb59-888bba14bebd",
"client_id": "example_client",
"iat": 1680653739,
"jti": "d760990f-62c9-4570-ac99-9e7477363e18",
"auth_time": 1680653739,
"scope": "iracing.auth",
"iracing_env": "members",
"iracing_cust_id": 15535,
"iracing_group_ids": [1, 2, 3, 5, 8, 10]
}
The fields are:
-
session_idThe UUID identifier of the session.
-
issThe issuer of the access token; the authorization server. This value MUST be a URL and is typically
https://oauth.iracing.com. -
expThe expiration time of the access token on or after which the access token MUST be rejected, in seconds since Thursday 1 January 1970 00:00:00 UTC.
-
audThe case-sensitive list of one or more audiences for which this access token is valid. The list MUST include both “oauth-api” and the
client_idof the client to which this access token was issued. -
sub(may be null)The subject of the access token.
Note
The
subclaim may benull. Access tokens are not intended for use as identity tokens. For user identity information, use /iracing/profile instead. -
client_idThe identifier of the client application.
-
iatThe time of issue of the access token in seconds since Thursday 1 January 1970 00:00:00 UTC. If the
iatclaim is in the future the token MUST be rejected. It is acceptable to allow a small tolerance for clock skew, typically no more than a few seconds, to account for minor time differences between servers. -
jtiA UUID identifying this access token, which may be used to detect and prevent replays.
-
auth_timeThe time at which the authentication and authorization process completed and the session was created, in seconds since Thursday 1 January 1970 00:00:00 UTC. If the
auth_timeclaim is in the future the token MUST be rejected. It is acceptable to allow a small tolerance for clock skew, typically no more than a few seconds, to account for minor time differences between servers. -
scope(may be null)One or more scopes granted to the session, if any, separated by white space. The session may not be granted any scopes, in which case this field is
null. -
iracing_env(may be null)The iRacing environment; typically “members”. This field is conditionally included. If the field is not
nulland the value is unexpected the access token MUST be rejected. -
iracing_cust_id(may be null)The numeric iRacing customer identifier of the subject. This field is conditionally included. If the field is not
nulland the value is unexpected the access token MUST be rejected. -
iracing_group_ids(may be null)Zero or more numeric iRacing group identifiers to which the subject belongs. This field is conditionally included. If the field is not
nulland the value is unexpected the access token MUST be rejected.
Validation and Verification
It is recommended that a library is used to help validate and verify access tokens. A well-behaved validation and verification process MUST complete both the signature verification and claims validation procedures.
Signature Verification
-
Ensure the token is a well-formed JWS in compact serialization format.
-
Ensure the header contains a
kidclaim, which is a non-empty string. -
Ensure the header contains an
algclaim, which is a non-empty string and not “none”. If the algorithm is not supported by the implementation, the token MUST be rejected.Warning
The
algclaim will never be “none”. This algorithm indicates that the token is not signed and MUST be rejected. This is a common attack vector against JWT implementations that fail to properly validate thealgclaim. -
Ensure the header contains a
jkuclaim, which is a non-empty string and a valid URL. Typically, this URL will be https://oauth.iracing.com/.well-known/jwks.json, which contains the public keys used to verify the token’s signature. This is a JSON Web Key Set (JWKS). The URL MUST use HTTPS, the client MUST verify the TLS certificate, and the URL MUST NOT contain user information, a query component, or a fragment component. Further, the domain component of the URL MUST end with the exact text.iracing.com. Do not follow redirects when fetching the JWKS. If the URL is not valid or does not meet these requirements, the token MUST be rejected. -
Verify that the JWKS at the
jkuURL contains one and only one key with akidclaim matching the token’skidclaim. -
Verify that the key identified by the
kidclaim is valid for the algorithm specified in thealgclaim. For example, if thealgclaim is “EdDSA”, the key must have aktyclaim of “OKP” and acrvclaim of “Ed25519”. Ignore any header-provided key material parameters including but not limited to thejwkparameter. -
Verify the token’s signature using the key identified by the
kidclaim. If the signature is invalid, the token MUST be rejected.
This is enough information to ensure that the token is valid, was issued by the authorization server, and has not been modified.
The claims MUST be validated before trusting their contents. The claims validation steps are described in the next section.
Claims Validation
Warning
The JOSE library should return the verified header and payload before proceeding. The client MUST use the verified header and payload returned by the library for the remaining validations.
Failure to use the verified header and payload can lead to security vulnerabilities, such as accepting tokens that are not properly signed, are expired, or are issued for a different audience.
The following additional checks should be performed to ensure the token is valid for the operational context:
- Ensure the token’s
expclaim is in the future. It is acceptable to allow a small tolerance for clock skew, typically no more than a few seconds, to account for minor time differences between servers. - Ensure the token’s
iatclaim is in the past. It is acceptable to allow a small tolerance for clock skew, typically no more than a few seconds, to account for minor time differences between servers. - Ensure the token’s
auth_timeclaim is in the past. It is acceptable to allow a small tolerance for clock skew, typically no more than a few seconds, to account for minor time differences between servers. - Ensure the token’s
iatclaim is no earlier than theauth_timeclaim. - Ensure the token’s
issclaim matches the expected issuer for the operational context. This is typicallyhttps://oauth.iracing.com. - Ensure the token’s
audclaim includes both “oauth-api” and theclient_idof the client to which this access token was issued. - Ensure the token’s
scopeclaim contains the required scopes. - Ensure the token’s
iracing_envclaim, if present, matches the expected iRacing environment for the operational context. This is typically “members”.
Refresh Token
A refresh token allows for the continuation of a session beyond the lifetime of an access token. A refresh token can be used in a Refresh Token Grant to obtain new tokens.
Important
Each refresh token may be used only once. After a refresh token is used, it expires and cannot be used again. The Refresh Token Grant will issue a new refresh token in the response, which the client must use to obtain the next set of tokens.
A refresh token is longer-lived than the access token. A refresh token is typically valid for 7 days, whereas the access token is typically valid for 10 minutes.
Clients must not rely on the lifetime values above. These lifetimes are the
defaults and may be changed on a per-client basis and at our discretion. The
/token response includes the expires_in and
refresh_token_expires_in fields, the lifetimes of the access and refresh
tokens in seconds. Clients should use these fields at runtime to determine when
to request new tokens.
The refresh token is considered opaque and its format may change without warning at our discretion.
/iracing/profile
A common use-case for this service is to provide identity verification of users to other applications.
If a client has an access token which has been granted the
iracing.profile scope it can call this endpoint for basic profile
information about the iRacing user.
GET https://oauth.iracing.com/oauth2/iracing/profile HTTP/1.1
Authorization: Bearer ...elided...
It has no parameters.
Response
It returns an application/json object:
{
"iracing_name": "John West",
"iracing_cust_id": 15535
}
The fields are:
-
iracing_nameThe name of the user.
-
iracing_cust_idThe numeric customer identifier of the user.
Authentication and Authorization
Clients will need to invoke /authorize to complete the
authentication and authorization flow. If this has been completed successfully,
the client will have received a code which it can use with
/token to retrieve an access_token and a refresh_token
if it is available.
- Start an authentication and authorization flow by directing the browser to
/authorize. Be sure to specify the correct
redirect_urifor the client. - The authentication and authorization flow ends by calling the
redirect_urifor the client with either acodeor error. - Use the received
codewith /token to obtain an access token and refresh token if available.
See the Tokens Overview for more information about the access and refresh tokens received in Step 3.
- If the access token has expired and a refresh token is available to the client, use /token with the refresh token to get new access and refresh tokens. A refresh token may only be used once.
- Call the resource server with the access token. The access token is sent to
the resource server as an HTTP
Authorizationheader prefixed by the textBearer.
/authorize
To begin an authentication and authorization flow, the client should direct the
user’s browser to the /authorize endpoint:
GET https://oauth.iracing.com/oauth2/authorize?client_id=as_issued&redirect_uri=https%3A%2F%2Fregistered-app.com%2Fcallback&response_type=code&code_challenge=...elided...&code_challenge_method=S256&state=...client_state_data...&scope=iracing.auth HTTP/1.1
Parameters
-
client_id(required)The client identifier issued during client registration.
-
redirect_uri(required)A redirect URI registered to the client, which must match exactly.
-
response_type(required)The only valid value for this is
code. -
code_challenge(usually required, always recommended)A PKCE code challenge. We require this of any client which cannot reasonably keep a secret, and encourage server-side applications to implement it regardless.
-
code_challenge_method(optional, defaults toplain)The PKCE code challenge method. Either
S256(recommended) orplain. -
state(optional, recommended)This state value will be returned unmodified at the end of the authentication and authorization flow. It may be used to store request-specific data and in the prevention of CSRF attacks.
-
scope(optional)One or more scopes to request, if any, separated by white space.
Tip
The server may add or remove scopes at its discretion or at the request of the user. Be sure to check the
scopein the token response. -
prompt(optional)Space-delimited, case-sensitive list of ASCII string values which influence how the authorization server interacts with the user. Values not in the following list are ignored:
-
“verify”
The authorization server should not honor any stored verifications; remembered devices should be forgotten.
-
Response
If authentication and authorization succeed, the browser will be redirected to
the given redirect_uri:
HTTP/1.1 302 Found
Location: https://registered-app.com/callback?code=3122eb8a-7a91-4dae-b174-07df2b083913&state=...client_state_data...
The following parameters are added to the query string of the redirect_uri:
-
codeThe authorization code which may be used with the /token endpoint. The value is considered opaque and its format may change without warning at our discretion.
-
stateIf a
statevalue was provided in the initial request, it will be returned unmodified for use by the client.
/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
codeit received at itsredirect_urifor tokens after it verifies thestateparameter. -
If a client possesses a refresh token, it may use this grant to trade the refresh token for a new tokens.
-
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 tokens.
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...
Tip
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_secretandredirect_uriparameters.
Parameters
-
grant_type(required)The value is “authorization_code” to trade in a
codefor 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.
The client secret must be masked before it is sent to the server.
-
code(required)As returned to the
redirect_uriof the client. -
redirect_uri(required)The same
redirect_uriused to /authorize. -
code_verifier(required for code challenge)The PKCE code verifier which is only required if a
code_challengewas used to /authorize.
Refresh Token Grant
If the client possesses a refresh token, it may trade the refresh token for new 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...
Tip
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_secretparameter.
Parameters
-
grant_type(required)The value is “refresh_token” to trade in a refresh token for new 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 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 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.
Warning
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 windowIf the client exceeds the rate limit, the server will respond with a
400 Bad Requeststatus code, an unauthorized_client error, and aRetry-Afterheader 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
Tip
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,passwordandscopeparameters.
Parameters
-
grant_type(required)The value is “password_limited” to use credentials to obtain tokens.
-
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 using the
client_idbefore it is sent to the server. -
username(required)The email address or other issued identifier for a user.
Note
This grant type requires an active iRacing subscription to use with iRacing services.
-
password(required)The password of the user.
The password must be masked with the
usernamebefore 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": 604800,
"scope": "iracing.auth"
}
The fields are:
-
access_tokenThe access token may be used to authorize a connection to a resource server.
-
token_typeThis will always be the string “Bearer”.
-
expires_inThe 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 tokens.
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.
Tip
The server may add or remove scopes at its discretion or at the request of the user.
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:
Node.js:
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");
}
Javascript (browser):
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)));
}
Java:
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.Locale;
public class Password {
public static String mask(String secret, String id) {
if (secret == null || id == null) {
throw new IllegalArgumentException("secret and id must not be null");
}
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
String normalizedId = id.trim().toLowerCase(Locale.ROOT);
String combined = secret + normalizedId;
byte[] hash = digest.digest(combined.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(hash);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("SHA-256 algorithm not available", e);
}
}
}
Python:
import base64
import hashlib
def _mask_secret(secret: str, identifier: str) -> str:
"""
Mask a secret (client_secret or password) using iRacing's masking algorithm.
Args:
secret: The secret to mask
identifier: client_id for client_secret, username for password
Returns:
Base64 encoded SHA-256 hash of secret + normalized_identifier
"""
# Normalize the identifier (trim and lowercase)
normalized_id = identifier.strip().lower()
# Concatenate secret with normalized identifier
combined = f"{secret}{normalized_id}"
hasher = hashlib.sha256()
hasher.update(combined.encode('utf-8'))
return base64.b64encode(hasher.digest()).decode('utf-8')
PKCE Challenges and Verifiers
PKCE Code Challenges and Verifiers are used for preventing Cross-Site Request Forgery and authorization code injection attacks. Learn more about them here.
An example of writing code for PKCE in Rust follows.
This code can be edited and run in the browser as an aid in debugging client implementations. Learn how to use this tool here.
use base64::prelude::{Engine, BASE64_URL_SAFE_NO_PAD};
use rand::{thread_rng, Rng};
use regex::Regex;
use sha2::{Digest, Sha256};
// This code is demonstrative, not performant.
// These are the characters that can be accepted as part of
// a code verifier. They are being stored as a byte array.
const CHARS: &[u8] = b"\
ABCDEFGHIJKLMNOPQRSTUVWXYZ\
abcdefghijklmnopqrstuvwxyz\
1234567890-._~";
/// Generate a code verifier of the given length.
///
/// # Panics
///
/// This code panics if the length is less than
/// 43 or greater than 128.
fn code_verifier(len: usize) -> String {
assert!(len >= 43);
assert!(len <= 128);
// Use a cryptographically strong random number generator.
let mut rng = thread_rng();
(0..len) // for each in 0 to len exclusive
.into_iter() // iterate
.map(|_| rng.gen_range(0..CHARS.len())) // get an index into CHARS
.map(|i| CHARS[i]) // get the byte at the index
.map(char::from) // map it back to a character
.collect() // collect the characters into a String
}
/// Generate a code challenge for the given verifier.
///
/// # Panics
///
/// This code panics if the given code verifier is less
/// than 43 characters long or greater than 128 characters.
fn code_challenge(code_verifier: String) -> String {
assert!(code_verifier.len() >= 43);
assert!(code_verifier.len() <= 128);
code_verifier
.chars() // get the characters in the String
.for_each(|c| { // for each character
assert!(CHARS.contains(&(c as u8))); // check that it is valid
});
// Prepare a SHA256 hash.
let mut sha256 = Sha256::new();
// Update the hash with the verifier.
sha256.update(code_verifier);
// Compute the final hash.
let result = sha256.finalize();
// Encode it as base64.
let base64 = base64::engine::general_purpose::STANDARD.encode(&result[..]);
// Make the base64 encoding "URL Safe" and remove the padding.
base64
.chars() // get the characters
.filter_map(|c| match c {
'=' => None, // remove padding
'+' => Some('-'), // make URL Safe
'/' => Some('_'), // make URL Safe
x => Some(x), // return anything else untouched
})
.collect() // collect it into a String
}
/// Validate a code verifier and challenge pair.
/// The value `true` is returned when valid.
///
/// This is another way of writing the `code_challenge` function.
fn is_valid(code_verifier: String, code_challenge: String) -> bool {
let re = Regex::new(r"^[A-Za-z0-9_\.\-\~]{43,128}$").unwrap();
if !re.is_match(&code_verifier) {
return false;
}
let digest = Sha256::digest(code_verifier.as_bytes());
let encoded = BASE64_URL_SAFE_NO_PAD.encode(digest);
// If the given verifier matches the challenge, the server
// will accept the pair.
encoded == code_challenge
}
fn main() {
/// Prefer the maximum.
let code_verifier = code_verifier(128);
// Override it with your own by uncommenting the following
// and replacing the contents of the string:
// let code_verifier = "5-Giz4oGgbRTt2Q2VmhQMKw_aTp9UJCQuD_~ZAlP-QM".to_string();
println!("code_verifier: {code_verifier}");
let code_challenge = code_challenge(code_verifier.clone());
// Override it with your own by uncommenting the following
// and replacing the contents of the string:
//let code_challenge = "-FG7uN-lx34GXN3xvKEPcwqoYnGX2R4ACX59z_X28vE".to_string();
println!("code_challenge: {code_challenge}");
if is_valid(code_verifier, code_challenge) {
println!("the server will accept this!");
} else {
println!("something has gone wrong");
}
}
Session Management
Some clients may wish to provide their own interface to let users discover connected sessions and revoke them. Several endpoints are provided to facilitate this.
Use /sessions to enumerate the client sessions for the user.
Use /revoke/current to log out the current session.
Use /revoke/sessions to log out one or more sessions for the user.
Use /revoke/client to log out all client sessions for the user.
/sessions
This endpoint may be used by a client to retrieve a list of sessions for the user.
Only the sessions for the calling client are returned.
GET https://oauth.iracing.com/oauth2/sessions HTTP/1.1
Authorization: Bearer ...elided...
It has no parameters.
Response
It responds with an application/json object:
{
"sessions": [
{
"session_id": "336995c9-67bd-4e5c-aa93-0d5e75c463d9",
"client_id": "client_identifier",
"client_name": "Client Name",
"client_developer_name": "Client Developer Name",
"client_developer_url": "http://localhost/",
"client_developer_email": "root@localhost",
"scope": "sessions",
"scope_descriptions": ["Session management."],
"auth_time": 1680276758,
"last_activity": 1680276828,
"session_expiration": 1680309158,
"current_session": true,
"impersonated": true,
"impersonation_note": "support for login test; approved by cust in #123456",
"first_ip": "192.168.23.103",
"first_continent": "North America",
"first_country": "United States",
"first_subdivisions": ["Massachusetts"],
"first_city": "Chelmsford",
"first_user_agent_header": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36",
"first_user_agent_operating_system": "Linux",
"first_user_agent_browser": "Chrome",
"last_ip": "192.168.23.103",
"last_continent": "North America",
"last_country": "United States",
"last_subdivisions": ["Massachusetts"],
"last_city": "Chelmsford",
"last_user_agent_header": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36",
"last_user_agent_operating_system": "Linux",
"last_user_agent_browser": "Chrome"
}
]
}
The fields are:
-
sessionsA list of one or more session objects detailing the user’s sessions.
Session Object
Each session object has the following fields:
-
session_idA session identifier. This value is considered opaque and its format may change without warning at our discretion.
-
client_idA client identifier.
-
client_nameThe name of the client as selected during client registration.
-
client_developer_name(may be null)The name of the client developer as captured during client registration, if any.
-
client_developer_url(may be null)The URL of the client developer as captured during client registration, if any.
-
client_developer_email(may be null)The email address of the client developer as captured during client registration, if any.
-
scope(may be null)One or more scopes granted to the session, if any, separated by white space. The session may not be granted any scopes, in which case this field will be
null. -
scope_descriptions(may be null)One or more descriptions for any scopes granted to the session, if any. The session may not be granted any scopes, in which case this field will be
null. -
auth_timeThe time at which the session was authenticated and authorized, in seconds since Thursday 1 January 1970 00:00:00 UTC.
-
last_activityThe time of the last activity of the session, in seconds since Thursday 1 January 1970 00:00:00 UTC.
-
session_expirationThe time at which the session will expire, in seconds since Thursday 1 January 1970 00:00:00 UTC.
-
current_sessionThe boolean value
trueif this represents the current session. -
impersonatedThe boolean value
trueif this session was created by a staff member instead of the user. -
impersonation_note(may be null)A note regarding the impersonation, detailing who is impersonating the user, why, and references to the customer approval.
-
first_ip(may be null)The first IP address used for the session.
-
first_continent(may be null)The continent from which the first activity of the session originated.
-
first_country(may be null)The country from which the first activity of the session originated.
-
first_subdivisions(may be null)The subdivisions of the country from which the first activity of the session originated.
-
first_city(may be null)The city from which the first activity of the session originated.
-
first_user_agent_header(may be null)The user agent header first used for the session.
-
first_user_agent_operating_system(may be null)The first operating system used for the session.
-
first_user_agent_browser(may be null)The first browser used for the session.
-
last_ip(may be null)The last IP address used for the session.
-
last_continent(may be null)The continent from which the last activity of the session originated.
-
last_country(may be null)The country from which the last activity of the session originated.
-
last_subdivisions(may be null)The subdivisions of the country from which the last activity of the session originated.
-
last_city(may be null)The city from which the last activity of the session originated.
-
last_user_agent_header(may be null)The user agent header last used for the session.
-
last_user_agent_operating_system(may be null)The last operating system used for the session.
-
last_user_agent_browser(may be null)The last browser used for the session.
/revoke/current
Revoke the current session. Any session may self-revoke. There are no parameters:
POST /oauth2/revoke/current HTTP/1.1
Host: oauth.iracing.com
Authorization: Bearer ...elided...
Content-Type: application/x-www-form-urlencoded
forget_browser=true
Parameters
-
forget_browser(optional)If set to
trueany 2FA Bypass Cookie previously generated for the browser will be removed. Defaults tofalse.
Response
It responds with no body. An HTTP status code of 200 indicates success.
/revoke/sessions
Revoke one or more client sessions by session identifier.
POST /oauth2/revoke/sessions HTTP/1.1
Host: oauth.iracing.com
Authorization: Bearer ...elided...
Content-Type: application/x-www-form-urlencoded
session_ids=cfd37ae2-7ad0-4b94-9d47-04c437e1545c,97e7ceb6-e7e6-4637-a2b8-a7a356b849de
Parameters
-
session_ids(required)The session identifiers to revoke.
Response
It responds with no body. An HTTP status code of 200 indicates success.
/revoke/client
Revoke all client sessions for the user.
The session must be a user session.
POST /oauth2/revoke/client HTTP/1.1
Host: oauth.iracing.com
Authorization: Bearer ...elided...
Response
It responds with no body. An HTTP status code of 200 indicates success.
Errors
Unless otherwise specified, errors from this server are returned either as a
redirect or as an application/json object in the response body.
Error Redirects
If the server can validate the identity of the client, the server will return
errors by redirecting to the redirect_uri with details.
HTTP/1.1 302 Found
Location: https://registered-app.com/callback?error=unsupported_response_type&error_description=Response%20type%20test%20not%20supported.&error_uri=https%3A%2F%2Foauth.iracing.com%2Foauth2%2Fbook%2Ferror_unsupported_response_type.html&state=...client_state_data...
-
errorA single ASCII error code, as below.
-
error_descriptionA textual description of the error, including more detail if possible.
-
error_uriA link to documentation.
-
state(optional)The unmodified
state, if any, from the /authorize call.
Error Responses
If the server cannot identify the client, the server will return errors as an
application/json object in the response body:
{
"status": 400,
"status_reason": "Bad Request",
"error": "invalid_request",
"error_description": "client_id missing",
"error_uri": "https://oauth.iracing.com/oauth2/book/errors.html"
}
The fields are:
-
statusThis reflects the HTTP status code, as below.
-
status_reasonA textual representation of the HTTP status code.
-
errorA single ASCII error code, as below.
-
error_descriptionA textual description of the error, including more detail if possible.
-
error_uriA link to documentation.
Error Codes
The error codes and their corresponding status codes are:
| error | status | status_reason | notes |
|---|---|---|---|
| access_denied | 401 | Unauthorized | |
| insufficient_scope | 403 | Forbidden | 2 |
| invalid_client | 403 | Unauthorized | |
| invalid_grant | 400 | Bad Request | |
| invalid_request | 400 | Bad Request | |
| invalid_scope | 400 | Bad Request | |
| invalid_token | 401 | Unauthorized | 2 |
| server_error | 500 | Internal Server Error | 1 |
| temporarily_unavailable | 503 | Service Unavailable | 1 |
| unauthorized_client | 401 | Unauthorized | |
| unsupported_grant_type | 400 | Bad Request | |
| unsupported_response_type | 400 | Bad Request |
Notes
- This error may be returned by /token. This is an extension to the OAuth 2.0 Specification as described in the errata.
- This error will not be returned by either /authorize or /token. This is an extension to the OAuth 2.0 Specification for use with other endpoints.
access_denied
The server or end-user has denied the request.
Explanation
This error is used to indicate that the server or end-user denied an otherwise valid request. This is opposed to unauthorized_client which signals a problem with how the client is attempting to use the server.
Remediation
This server will provide an error_description to help understand why the
error was returned. Common causes are:
- the session has expired, become invalid, or otherwise no longer exists
- the user denied the client’s scope request
- the supplied credentials were not correct
- a request was made for a resource disallowed to the user
insufficient_scope
The scope of the access token does not include authorization for the requested resource.
Explanation
Some operations require that the end-user has delegated authority to the client before the client is allowed to proceed. These delegations are captured as the scope of the access token.
The server may at its discretion not honor a delegation that the end-user has approved.
Remediation
An access token cannot gain new privileges; a new access token will need to be obtained before the operation can proceed.
invalid_client
Client authentication failed.
Explanation
Clients are authenticated by the values of certain parameters. One or more of these mechanisms were not correct.
Remediation
This error is encountered when:
- the
client_idis not valid - the
redirect_uridoes not match exactly - the
client_secretif issued is incorrect
Note
The
redirect_uriandclient_secretshould be URL (percent) encoded when submitting them to /token. Additionally, theclient_secretmust be properly masked before it is URL encoded.
invalid_grant
The provided authorization code or refresh token is invalid.
Explanation
The client attempted to use a code or refresh_token with the
/token which the server rejected.
Remediation
The code or refresh_token should be discarded.
This error occurs when:
- a token or session has become invalid or was revoked
- PKCE verification fails
- a request is made that needs stronger authentication
- a subscription is made inactive
invalid_request
The request was malformed in some way; an invalid parameter value was used, a parameter was repeated, the format did not parse, or some other such error.
Explanation
The server detected an error with the request. The server will provide
additional detail in the error_description field.
Remediation
This error is widely used throughout the service, so the error_description is
the best way to correct the error.
Common reasons this error occurs are:
- the request could not be parsed; most endpoints expect an
application/x-www-form-urlencodedform - the form data did not include all required fields
- the form data supplied an incorrect or invalid value for a field
- the resource is in a state for which the request is not valid
invalid_scope
The given scope is invalid, unknown, malformed, or disallowed.
Explanation
See the documentation on scopes for which scopes are available and to what purposes.
Remediation
Adjust the scope to be within the documented scopes.
This error occurs when:
- the scope contains unknown components
- the scope has duplicated components
- the scope has disallowed characters
- the scope requests access to which the user is not privileged
invalid_token
The token is invalid, malformed, or missing.
Explanation
The client attempted to use a token which the server has rejected. The
error_description will reveal more about why the token was rejected.
Remediation
It is unlikely that a token which has been deemed invalid will ever become valid again. It is best to discard the token and start again.
This error is encountered when:
- the token expires
- the token contains data that is no longer valid
server_error
The server encountered an internal error. The request has not been fulfilled.
Explanation
The request was received by the server and processing was attempted. An error was caught by the server, and the server was not able to recover.
Remediation
Retry the operation at a later time. These errors are often transient. If the error persists, please report the issue.
temporarily_unavailable
The server is unavailable.
Explanation
The server has been made unavailable for maintenance. This should be a relatively rare occurrence.
Remediation
Retry the operation at a later time. If the error persists, please report the issue.
unauthorized_client
The client is not allowed to perform this operation.
Explanation
This error is used to signal a problem with the how the client is attempting to use the server. This is opposed to access_denied which indicates that the server or end-user denied an otherwise valid request.
Remediation
This is most often triggered by:
-
a bad
client_idparameterCheck that the client is using the exact value assigned during client registration.
-
a bad
redirect_uriparameterCheck that the redirect_uri matches exactly one of the values supplied during client registration.
-
a rate limit which has been exceeded
This is most often seen with the Password Limited Grant which is expected to be used only once at client start-up. Use the Refresh Token grant to maintain the access token. See the warning in the documentation for the Password Limited Grant for more information.
unsupported_grant_type
The grant type requested by the client is not supported.
Explanation
The server only accepts requests for a limited number of grant types.
Remediation
The documentation for /token details the accepted grant types.
unsupported_response_type
The response type requested by the client is not supported.
Explanation
The server only accepts requests for a limited number of response types.
Remediation
The documentation for /authorize details the accepted response types.
Reporting Issues
If you have an x-request-id, please include it in your message.
| Purpose | Email Address |
|---|---|
| Client requests, support, other inquiries | auth@iracing.com |
| Security Issues | infosec@iracing.com |
Please email auth@iracing.com for client requests, support, or other inquiries.
Security issues with this or any other iRacing.com service should be reported to infosec@iracing.com.
Release Notes
2025-09-05
- The
/tokenendpoint will now omit the optionalrefresh_token,refresh_token_expires_in, andscopefields from its JSON response if they are not present. Previously, these fields were included with a value ofnull.