Seamless authentication

Last updated: February 21, 2023

Introduction

The seamless exchange allows a Décathlon mobile app

  • used by a member
  • where the member is authentified without implicit-flow
  • whose SSO session may be terminated

to identify seamlessly the member in the webviews integrated to the Décathlon mobile app for several usages:

  • Payment access
  • Loyalty points
  • ...

Important

The seamless-exchange flow is subject to strict governance and will not be granted without justification.

Description

The seamless-exchange process has two major actors:

  • The Decathlon Mobile App (with client_id A)
  • A webview with access to targeted resources which will be accessed thanks to the exchanged token (with client_id B)

The process must respect the reference specification: rfc icon RFC 8693: OAuth 2.0 Token Exchange

Token Exchange flow between mobile app with client_id_A and webview with client_id_B
  • The member is authenticated on mobile app (client A) with an id_token.
  • This id_token is given to the webview (client B) included in the mobile app.
  • The webview makes a call to its backend with the id_token so that the backend can exchange it via token-exchange with an access (plus refresh) token to access a resource.
  • The webview backend can then access the resource with the exchanged token.

Tokens details

Exchangeable id token

The exchangeable token is an id_token with:

  • The member-id of the user;
  • The client-id of the client who can exchange this token with a valid member access token;
  • The scopes of the futur member access token.

How to implement ?

Exchange the token (client B)


It is strongly recommended for the webview backend which exchange the token to use a separate client-id with the specific grant type client_credentials. It prevents the generation of an access token with too wide scopes.

Prerequisites

  • The client exchanging the id token (client B) must have the scope token-exchange:seamless;
  • To ask for an exchangeable token, the client must be authenticated with a basic or bearer token. It must only have the client_credentials grant_type configured;

How to get a client-credentials token


Details of the call

POST /connect/token/exchange

Headers:
Name Description Required
Authorization Two choices:
  • 'Bearer ' + access token from a client credentials authentication;
  • 'Basic ' + CLIENT_ID:CLIENT_SECRET encoded as base64
Yes
Content-type application/json Yes

Body:
Parameter Description Required
grant_type urn:ietf:params:oauth:grant-type:token-exchange Yes
exchange_type urn:decathlon:params:oauth:exchange-type:seamless-exchange Yes
requested_token_type If present, must be set to the following value: urn:ietf:params:oauth:token-type:access_token No
subject_token_type urn:ietf:params:oauth:token-type:id_token Yes
subject_token The id token of the member.
  • Its signature must be valid.
  • The issuer must be Decathlon login.
  • The token is not expired and must be recent (an id token max age will be parametered on token exchange with a default value of 2 minutes).
  • The audience must contain /token/exchange.
  • The audience must contain the id of client B (the client which exchanges the token).
  • [Optional] The id of client A must be parameterized to allow seamless exchanges (Not implemented).
  • [Optional] The id token hasn't already been used for a previous exchange (Not implemented).
Yes
scopes A list of scopes to set in the exchangeable token.
If specified, the scopes of the client B must contain every scope of this parameter (if a requested scope is not in the client list, an error will be thrown).
If not specified, all the scopes of the client B are set in the exchangeable token.
No

Response:

In case of success, regarding the specification rfc icon RFC 8693: OAuth 2.0 Token Exchange , the response will have the following characteristics:

  • HTTP Status : 200 OK
  • Content-Type : application/json;charset=UTF-8
  • Header : Cache-Control: no-store
  • Header : Pragma: no-cache

Detail of the attributes contained in the JSON response body

Name Description Required
access_token A standard access token. It's neither an impersonation nor a delagation. Then, the claims act and may_act won't be used. Yes
issued_token_type urn:ietf:params:oauth:token-type:access_token Yes
token_type bearer Yes
expires_in token validity, in seconds Yes
scope scopes list included in the access token, space separated Yes
refresh_token If configured for the client, a refresh token allowing to obtain a new access-token with the same scopes and audience No

Any missing mandatory parameter generates an invalid_request HTTP 400 error.
Any parameter with an invalid value generates an invalid_request HTTP 400 error.
Some forbidden parameters can also generate an invalid_request HTTP 400 error if present: resource, audience, actor_token_type, actor_token.

Example of a successful call:

curl  -X POST --location 'https://api-global.preprod.decathlon.net/connect/token-exchange' \
--header 'Authorization: Basic BASE64_AUTH' \
--header 'Content-Type: application/json' \
--data DATA
                                                
                                            

Example of a successful call with values:

curl  -X POST --location 'https://api-global.preprod.decathlon.net/connect/token-exchange' \
--header 'Authorization: Basic Y2xpZW50LXRva2VuLWV4Y2hhbmdlLXNlYW1sZXNzOmNsaWVudC10b2tlbi1leGNoYW5nZS1zZWFtbGVzcy1zZWNyZXQ=' \
--header 'Content-Type: application/json' \
--data '{
"grant_type":"urn:ietf:params:oauth:grant-type:token-exchange",
"exchange_type":"urn:decathlon:params:oauth:exchange-type:seamless-exchange",
"requested_token_type":"urn:ietf:params:oauth:token-type:access_token",
"subject_token_type":"urn:ietf:params:oauth:token-type:id_token",
"subject_token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJmNTk2NzYwZC00ZGFjLTQxNTItOGMzMS0zOWY1ZjhhYmE0NTAiLCJhdWQiOlsiL3Rva2VuL2V4Y2hhbmdlIiwiY2xpZW50LXRva2VuLWV4Y2hhbmdlLXNlYW1sZXNzIiwiY2xpZW50LXNlYW1sZXNzIl0sImF6cCI6ImNsaWVudC1zZWFtbGVzcyIsImF1dGhfdGltZSI6MTY3NzU5NTc1NywiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo5OTk5IiwiZXhwIjoxNjc3NTk2NjU3LCJpYXQiOjE2Nzc1OTU3NTcsImp0aSI6IklELmFFMHdmNEdnN1lSSTFpNlU4NVIxOTJuMkplcyJ9.TYHo539OjPZwGPw2f_w28KAIqA2H352p3rYWVZqeFv6vnJ4U0r9XFxcBhKwJfUi1s33DLkxd50zHb8TO5RT1hS9IdqKpUFQ-m0IeVZWG8IuKyLIDD6YefxA7zR2ChXIl-PuyXOWdk-PThToJA0JbXfwzceV2mCgTdyCq3zw1ETJ54kwT3vM47TPi8I7qGiNodp1j6IQIMqHivphVrsxH05mJMMzCXy86ZqP6zmrdUBm8tXf2NHRSO03Svf79lRNQN-NBN9pJLnhWxQ_mE1pI9vrjR4ifmS0NB1qphhq_8bvJz37MgmughUmzotAq1BaqYSn6ECROcU4fW1lcIqD8LA",
}'
                                                
                                            

In response you will get a JSON object


{
  "access_token": "eyJraWQiOiJtYWluIiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJmNTk2NzYwZC00ZGFjLTQxNTItOGMzMS0zOWY1ZjhhYmE0NTAiLCJuYmYiOjE2Nzc1OTU1NzAsInNjb3BlIjpbIm9wZW5pZCIsInByb2ZpbGUiLCJlbWFpbCIsImNvbnRhY3RzIl0sImlzcyI6Imh0dHA6XC9cL2xvY2FsaG9zdDo5OTk5IiwibG9jYXRpb24iOiJGUiIsImV4cCI6MTY3NzU5NjQ3MCwiaWF0IjoxNjc3NTk1NTcwLCJqdGkiOiIwYWFjZjZhYi03YjZhLTRkNmMtOTY1MC02ZjgxM2FlNjVhZjMiLCJjbGllbnRfaWQiOiJjbGllbnQtdG9rZW4tZXhjaGFuZ2Utc2VhbWxlc3MifQ.OJn_nkIlJRmIvsHLJKg6ruvycYTT5i2Yi6iimJuw1E53cDpFN9Bjy9NQqAOePtLjrVt56rap-y-hCTGMmX68uM5yfcnN6mCMZRHe19R57hpmEv2iXTLS4sj7JytvH5plKW6SfeogfoQGmIaXRsDsV4-OQNN5Hj3HDbozNyWKt6NXYgHpf4RXgyRmXCZKtqYZAiwMBIgCwkIJaz0fjlUZ7QsomhbL3-WIEEcs7SebqTuyskWvmku2he9Frhi1Xd2-3eiEUscYOdM4KYMZ1X8BCyD0ARg2oLUvXlKurt_XLEB9vI6SzsvaBJl8K0ZK4sd-Oj3TspnVKGcJSgK62W82hA",
  "issued_token_type": "urn:ietf:params:oauth:token-type:access_token",
  "scope": "openid profile email contacts",
  "token_type": "Bearer",
  "expires_in": 899
}
                                                
                                            
Terms & Services