Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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.

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.

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.

IconDescription
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.

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:

IconDescription
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:

  • GET

    This is an HTTP GET request, as opposed to POST or 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=false

    This 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/endpoint followed by the parameters example with the value true and another with the value false.

  • HTTP/1.1

    This 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 Authorization header 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:

  • POST

    This is an HTTP POST request, as opposed to GET or 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/endpoint

    This 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/endpoint as it would be referenced in this book.

  • HTTP/1.1

    This is an implementation detail of the HTTP protocol, specifying its version. Most clients will allow for this to be omitted.

  • Host: oauth.iracing.com

    This 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 Authorization header 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-urlencoded

    A Content-Type header tells the developer what format the API is expecting for any data submitted in the POST body. The API uses the application/x-www-form-urlencoded for the POST body format whenever possible.

  • example=true&another=false

    A blank line separates the last header from the POST body data. In this case, the data are the parameters example with the value true and another with the value false.

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.1

    This is an implementation detail of the HTTP protocol, specifying its version.

  • 307

    The HTTP status code.

  • Temporary Redirect

    The 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=false

    This 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/endpoint followed by the parameters example with the value true and another with the value false.

Error Responses

Error responses may be in any of the above formats. See the Errors chapter for more information.

Client Registration

Client registration is open to all interested parties. Please contact us to get started.

To register a client, we will require the following data:

  • Client Name

    The name of the client, which will be displayed to users.

  • Client Type

    Will it be server-side, a single-page app, mobile, or native?

  • Developer's Name

    To be displayed to users.

  • Developer's URL

    To be displayed to users.

  • Developer's Email

    To be displayed to users.

  • Redirect URIs

    The list of URIs allowed for redirects when authentication is completed. Query parameters are allowed but there must be no content in the fragment. These must match exactly during the authentication process.

    See the documentation on Redirect URIs for more information.

  • Audiences

    The iRacing.com service with which the client will interact beyond basic authentication, if any. Most often, this will be data-server to access https://members-ng.iracing.com/data.

Once the client is created, we will issue the following data for use in the client:

  • client_id

    The identifier of the newly created client.

  • client_secret

    Only issued to server-side clients.

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.

workflows

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

  1. Obtain the iRacing customer identifier or display name for a user.

Prerequisites and Assumptions

  1. The client has been registered.
  2. No refresh tokens will be issued and the access token will be used only once so there is no need to store it.
  3. The user has created an account within the client and has authenticated within the client.

Steps

  1. The client starts an Authentication and Authorization flow by invoking /authorize. The client should request the scope iracing.profile to get access to the iRacing profile and specify the correct redirect_uri registered to the client.
  2. 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_uri with a code.
  3. The client receives the code at its redirect_uri. It may then use /token to trade the code for an access token.
  4. 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.
  5. 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

  1. Use the full Data API without handling user passwords.

Prerequisites and Assumptions

  1. The client has been registered with an audience of data-server.
  2. The client can store access and refresh tokens securely.

Steps

  1. 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 Bearer token in the Authorization header while making requests to the Data API. It may continue to make requests until the access token expires or is otherwise invalidated.
  2. 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 a new access token and a new refresh token. The client securely stores the new tokens. Return to Step 1.
  3. 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.auth to allow making requests as the authenticated user and specify the correct redirect_uri registered to the client.
  4. 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_uri with a code.
  5. The client receives the code at its redirect_uri. It may then use /token to trade the code for an access token and a refresh token. The client securely stores the new tokens. Return to Step 1.

Authentication Flows

This service supports multiple ways to authenticate:

authentication flows

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 an access token.

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). Whenever possible, the Authorization Code Flow should 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.

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 types

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.

client roles

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

  1. 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.
  2. A well-formed Redirect URI will have at least 3 slashes; "https://app.yourdomain.example/" is valid but "https://app.yourdomain.example" is not.
  3. The "https://" scheme is strongly preferred over "http://"; additional justification may be required before an "http://" Redirect URI is configured.
  4. 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.
  5. 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.
  6. Port numbers may be specified. The Redirect URI "http://127.0.0.1:12345/callback" is valid.
  7. 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.
  8. Any arbitrary path may be used. We prefer paths such as "/", "/callback", "/redirect", or "/login".
  9. 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.
  10. 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

  1. The scheme MUST be "http://"
  2. The host MUST be the IPv4 (127.0.0.1) or IPv6 ([::1]) loopback address; "localhost" or equivalents will not be accepted.
  3. The port MUST be 0.
  4. The other General Rules still hold.

A native application may:

  1. start a web server bound to an ephemeral port on the loopback device;
  2. wait for a callback on the web server in the background; then
  3. start a web browser, or have the user start a browser, submitting a request to /authorize with a redirect_uri that substitutes 0 with 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 may be disabled. This helps to prevent unauthorized access from forgotten or abandoned clients.

Clients that have been inactive for 180 days will be disabled. Disabled clients cannot be used to obtain new tokens. The Developer's Email address provided during client registration will be used to notify the developer of inactivity and subsequent disabling.

A warning email will be sent after the client has been inactive for 90 days, 120 days, and 150 days. If the client remains inactive for 180 days, it will be disabled and a final notification email will be sent.

Clients which remain disabled for an extended period may be permanently deleted.

To reactivate a disabled client or to have a client disabled, 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.

CORS

This service supports CORS.

If the client has been configured for CORS, the API will set the Access-Crontrol-Allow-Origin header.

Otherwise, this header will not be present.

In the event of an internal error, the server assumes the client is not configured.

The Access-Control-Allow-Credentials header will be set if allowed.

To have an origin added for a client, report an issue.

Scopes

The following scopes may be requested:

scopedescription
iracing.authAuthorization for iRacing services.
iracing.profileAccess to the iRacing profile.

The iRacing.com Auth Service may not honor all requested scopes. At its discretion, other scopes may be included.

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.

auth

  1. Start an authentication and authorization flow by directing the browser to /authorize. Be sure to specify the correct redirect_uri for the client.
  2. The authentication and authorization flow ends by calling the redirect_uri for the client with either a code or error.
  3. Use the received code with /token to obtain an access token and refresh token if available.

Access tokens are typically short-lived, lasting only minutes, hours, or days. Refresh tokens are typically much longer-lived. After the initial token acquisition, if a client has a refresh token it may use it to acquire new access and refresh tokens when the access token expires. Refresh tokens may be used only once.

use

  1. 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.
  2. Call the resource server with the access token. The access token is sent to the resource server as an HTTP Authorization header prefixed by the text Bearer.

/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 identifer 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 to plain)

    The PKCE code challenge method. Either S256 (recommended) or plain.

  • 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 scope in 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:

  • code

    The 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.

  • state

    If a state value was provided in the initial request, it will be returned unmodified for use by the client.

/token

The /token endpoint supports the following grants:

  • Authorization Code Grant

    This is the last step of the authentication and authorization flow. A client may use this grant to trade the code it received at its redirect_uri for an access token and, at the discretion of the server, a refresh token after if verifies the state parameter.

  • Refresh Token Grant

    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.

  • Password Limited Grant

    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...

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_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...

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 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.

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 window
  • RateLimit-Remaining: the number of requests remaining in the current time window
  • RateLimit-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

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, 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.

    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:

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)));
}

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:

  • sessions

    A list of one or more session objects detailing the user's sessions.

Session Object

Each session object has the following fields:

  • session_id

    A session identifier. This value is considered opaque and its format may change without warning at our discretion.

  • client_id

    A client identifier.

  • client_name

    The 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_time

    The time at which the session was authenticated and authorized, in seconds since Thursday 1 January 1970 00:00:00 UTC.

  • last_activity

    The time of the last activity of the session, in seconds since Thursday 1 January 1970 00:00:00 UTC.

  • session_expiration

    The time at which the session will expire, in seconds since Thursday 1 January 1970 00:00:00 UTC.

  • current_session

    The boolean value true if this represents the current session.

  • impersonated

    The boolean value true if 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 true any 2FA Bypass Cookie previously generated for the browser will be removed. Defaults to false.

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.

/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_name

    The name of the user.

  • iracing_cust_id

    The numeric customer identifier of the user.

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...
  • error

    A single ASCII error code, as below.

  • error_description

    A textual description of the error, including more detail if possible.

  • error_uri

    A 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:

  • status

    This reflects the HTTP status code, as below.

  • status_reason

    A textual representation of the HTTP status code.

  • error

    A single ASCII error code, as below.

  • error_description

    A textual description of the error, including more detail if possible.

  • error_uri

    A link to documentation.

Error Codes

The error codes and their corresponding status codes are:

errorstatusstatus_reasonnotes
access_denied401Unauthorized
insufficient_scope403Forbidden2
invalid_client403Unauthorized
invalid_grant400Bad Request
invalid_request400Bad Request
invalid_scope400Bad Request
invalid_token401Unauthorized2
server_error500Internal Server Error1
temporarily_unavailable503Service Unavailable1
unauthorized_client401Unauthorized
unsupported_grant_type400Bad Request
unsupported_response_type400Bad Request

Notes

  1. This error may be returned by /token. This is an extension to the OAuth 2.0 Specification as described in the errata.
  2. 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_id is not valid
  • the redirect_uri does not match exactly
  • the client_secret if issued is incorrect

Info

The redirect_uri and client_secret should be URL (percent) encoded when submitting them to /token. Additionally, the client_secret must 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-urlencoded form
  • 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 access token or refresh 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_id parameter

    Check that the client is using the exact value assigned during client registration.

  • a bad redirect_uri parameter

    Check 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.

PurposeEmail Address
Client requests, support, other inquiriesauth@iracing.com
Security Issuesinfosec@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 /token endpoint will now omit the optional refresh_token, refresh_token_expires_in, and scope fields from its JSON response if they are not present. Previously, these fields were included with a value of null.