Using Refresh Tokens
This chapter explains how to use refresh tokens in TMS Sphinx to allow client applications to obtain new access tokens without requiring user re-authentication.
What are Refresh Tokens?
Refresh tokens are long-lived credentials that can be exchanged for new access tokens without requiring the user to log in again. They are particularly useful for:
- Native applications that need to maintain long-term access
- Web applications that need to access APIs on behalf of users over extended periods
- Any scenario where access tokens expire but you want to maintain authorization
Access tokens typically have a short lifetime (e.g., 1 hour) for security reasons. When an access token expires, instead of forcing the user to log in again, the client application can use a refresh token to obtain a new access token silently.
Enabling Refresh Tokens
To enable refresh tokens in your Sphinx server, you need to:
Allow the refresh_token grant type for your client application by adding
gtRefreshTokento the TSphinxClientApp.AllowedGrantTypes property.Request the
offline_accessscope when initiating the authorization flow. Sphinx only issues refresh tokens when this scope is present in the authorization request.
Here's an example of configuring a client application to support refresh tokens:
Client := SphinxConfig1.Clients.Add;
Client.ClientId := 'myclient';
Client.DisplayName := 'My Application';
Client.RequireClientSecret := False;
Client.AllowedGrantTypes := [TGrantType.gtAuthorizationCode, TGrantType.gtRefreshToken];
Client.ValidScopes.Add('openid');
Client.ValidScopes.Add('email');
Client.ValidScopes.Add('offline_access'); // Required for refresh tokens
Obtaining a Refresh Token
Refresh tokens are only issued during the authorization code flow. When your client application exchanges an authorization code for tokens, and the offline_access scope was requested, the token endpoint response will include a refresh_token in addition to the access_token and (if openid scope was requested) id_token:
{
"access_token": "eyJhbGc...",
"id_token": "eyJhbGc...",
"refresh_token": "rt_abc123...",
"token_type": "Bearer",
"expires_in": 3600
}
Important security considerations:
- Refresh tokens are only issued for authorization code flow. They are never issued for implicit flow or client credentials flow.
- The
offline_accessscope must be included in the initial authorization request. - Refresh tokens are bound to the specific client application that requested them.
Using a Refresh Token
To obtain a new access token using a refresh token, send a POST request to the token endpoint (<base_url>/oauth/token) with the following parameters:
grant_type: Must berefresh_tokenrefresh_token: The refresh token value received previouslyclient_id: The client application identifierscope(optional): Space-separated list of scopes. Can only reduce scopes, not expand them.
If client authentication is required (TSphinxClientApp.RequireClientSecret is True), you must also provide the client credentials.
Example request:
POST /oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token&
refresh_token=rt_abc123...&
client_id=myclient
The response will include a new access token and, if the openid scope is present, a new id_token:
{
"access_token": "eyJhbGc...",
"id_token": "eyJhbGc...",
"refresh_token": "rt_xyz789...",
"token_type": "Bearer",
"expires_in": 3600
}
Refresh Token Rotation
For enhanced security, TMS Sphinx implements refresh token rotation. This means:
- Each time a refresh token is used, it is marked as consumed and invalidated
- A new refresh token is issued in the response
- The old refresh token cannot be used again
This rotation mechanism helps detect token theft. If a refresh token is used more than once, it indicates either:
- The token was stolen and is being used by an attacker
- The client application has a bug where it's reusing old tokens
In either case, when a consumed refresh token is reused, Sphinx will:
- Reject the token request with an
invalid_granterror - Return an error indicating the token was already consumed
Best Practice: Always store the most recent refresh token and discard the old one after obtaining a new token.
Scope Reduction
When using a refresh token, you can optionally reduce the scopes in the new access token by providing a scope parameter that is a subset of the original scopes. However, you cannot request additional scopes beyond what was originally granted.
Example:
// Original authorization requested: openid email profile
// Refresh token request can use: openid email (reduced)
// Refresh token request CANNOT use: openid email profile customers (expanded)
If you don't provide a scope parameter, the new access token will have the same scopes as the original authorization.
Token Lifetime
Refresh tokens have a default lifetime of 30 days. You can customize this by changing the value of property TSphinxClientApp.RefreshTokenLifetime method in your client facade, or by setting it in your custom implementation if you're not using the built-in configuration.
Access tokens issued via refresh token grant will have the same lifetime as those issued via authorization code grant, as configured in TSphinxClientApp.AccessTokenLifetime.
Security Recommendations
When implementing refresh tokens in your application:
Store refresh tokens securely: They are long-lived credentials and should be treated with the same care as passwords.
Use HTTPS: Always transmit refresh tokens over HTTPS to prevent interception.
Implement token rotation: Sphinx implements this by default - always use the new refresh token and discard the old one.
Request minimum necessary scopes: Only request the scopes your application actually needs.
Error Handling
When using refresh tokens, you may encounter these common errors:
- invalid_grant: The refresh token is invalid, expired, revoked, or has already been used
- invalid_scope: The requested scopes are not a subset of the original scopes
- unauthorized_client: The client is not authorized to use the refresh_token grant type
- invalid_client: Client authentication failed
Your application should handle these errors gracefully, typically by requiring the user to log in again to obtain a new refresh token.