Quickstart - Enroll End-Users
Requirement:
- 👤 A registered Application in Stellantis systems and its credentials.
- 🔑 Your Application private key.
Tutorial Output:
- 🪙 An Access Token allowing requesting data about a user enrolled in your App.
This tutorial explains how to enroll an End User in order to obtain an Access Token using the OAuth2 Authorization Code Flow. This token allows requesting data about the user.
First, we will need to verify if the vehicle is eligible for your use case. Then, we will see how to implement the OAuth2 Authorization Code Flow by creating the consent request in your App and finally, to exchange the Authorization Code against the access token.
Manufacturers Brands & Realms #
In this tutorial API calls require the manufacturer {brand.tld} or <realm>. They both depends on the vehicle brand and should be retrieved on your side based on the vehicle VIN. Available realms are:
Manufacturer Brand | {brand.tld} | <realm> |
---|---|---|
Peugeot | peugeot.com | clientsB2CPeugot/OTPPeugeot |
Citroën | citroen.com | clientsB2CCitroen/OTPCitroen |
DS | driveds.com | clientsB2CDS/OTPDS |
Opel | opel.com | clientsB2COpel/OTPOpel |
Vauxhall | vauxhall.co.uk | clientsB2CVauxhall/OTPVauxhall |
1️⃣ Vehicle Capabilities #
First of all, before enrolling an end user, you must make sure that the targeted vehicle is eligible to your application use case.
Indeed, as explained in Data Availability & Scopes, all vehicles are not able to generate all the data listed in API Reference. When data is not generated by a vehicle, the field or object is not outputted in the API response.
That’s why you need to verify that a vehicle is able to provide the data required for your application use case. It can be done using the capability API.
The capability API will return the technical capabilities of the vehicles in terms of datasets. These capability datasets are subsets of vehicles scopes. Relations between APIs endpoints, data & scopes can be found in the Data Catalog (each data has a “B2C Scopes” section) and the API Reference (APIs endpoints have an “authorization>scope” dropdown).
Using the response of the capability API, and knowing the required data for your use case, you can determine whether the vehicle is eligible to your service or not. If the vehicle is not eligible, you must not enroll the user under this use case, but you can find a fallback use case fitting the vehicle capabilities.
For example if your use case requires the vehicle location, but the scope data:position
is not returned in the capability API response for this vehicle, then you should probably not offer your service to this user.
Technical capability & Vehicle eligibility: a data being available within the capability API doesn’t mean that the user is able to subscribe third party services, checkout vehicle services.
Capability API Example
Type | Name | Value | Description | Required |
---|---|---|---|---|
Path Parameter | {vin} |
{vin} |
Vehicle VIN to check. | Yes |
Query Parameter | client_id |
<client_id> |
Client Id of your application. | Yes |
Query Parameter | extension |
onboardCapabilities |
Filters only capability data of this API. | No |
Header | x-introspect-realm |
<realm> |
Vehicle manufacturer realm. | Yes |
File | Client Certificate | path/to/client_certificate.pem |
Your SSL certificate for authentication in Stelantis network. | Yes |
File | Private Key | path/to/key.pem |
Your Private Key file. | Yes |
File | CA Certificate | path/to/ca_certificate.pem |
Stellantis CA Cert for peer verification. | Yes |
1
2
3
4
5
6
7
8
9
$ curl \
--request GET \
--url 'https://api-cert.groupe-psa.com/connectedcar/v3/vehicle/{vin}' \
--data-urlencode 'client_id=<client_id>' \
--data-urlencode 'extension=onboardCapabilities' \
--header 'x-introspect-realm: <realm>' \
--key 'path/to/key.pem' \
--cert 'path/to/client_cert.pem[:<cert_password>]' \
--cacert 'path/to/ca_cert.pem' \
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
HTTP/1.1 200 OK
Date: Day, XX Mon YYYY HH:MM:SS GMT
Content-Type: application/json
{
"createdAt": "2023-09-12T09:41:10.911Z",
"vin": "VR1AB12C3D45678909",
"motorization": "Thermic",
"attributes": {...},
"_embedded": {
"extension": {
"onboardCapabilities": {
"data": [
"data:telemetry", "telemetry:environment", "data:telemetry:privacy",
"data:telemetry:vehicle", "data:telemetry:vehicle:ignition", "data:telemetry:vehicle:preconditioning",
"data:telemetry:vehicle:energies", "data:telemetry:vehicle:engines", "data:telemetry:vehicle:doorsState",
"data:telemetry:vehicle:powertrain", "data:telemetry:vehicle:battery", "data:telemetry:vehicle:safety",
"data:telemetry:vehicle:odometer", "data:telemetry:vehicle:kinetic", "data:telemetry:vehicle:transmission",
"data:telemetry:vehicle:adas", "data:telemetry:vehicle:lightingSystem", "data:telemetry:vehicle:maintenance",
"data:telemetry:vehicle:drivingBehavior", "data:telemetry:vehicle:wipingBlades", "data:telemetry:vehicle:alarm",
"data:position", "data:trip", "data:alert", "data:collision"
],
"remote": {
"operation": [
"remote:door:write", "remote:preconditioning:write",
"remote:horn:write", "remote:charging:write",
"remote:lights:write", "remote:wakeup:write", "remote:navigation:write"
],
"details": {
"charging": {
"supported": true,
"immediate": { "start": true, "stop": true },
"schedule": { "programs": {"supported": true,"size": 0}, "nextDelayedTime": true },
"preferences": { "level": true, "type": true }
},
"preconditioning": { "supported": true, "programs": { "size": 0 } },
"door": { "supported": true },
"horn": { "supported": true },
"lights": { "supported": true },
"wakeup": { "supported": true },
"navigation": { "supported": true }
}
}
}
}
}
}
2️⃣ Request Consent #
2️⃣•1️⃣ Authorization Request #
In order to enroll an End User, you have to trigger the following HTTP authorization request from your App in the End User device. This process is called OAuth2 Authorization Code Flow and is widely defined in RFC 6749.
This will prompt an external user agent (web view) where the End User will be able to log in and authorize access to its Stellantis account. The following parameters are required:
Scopes: scopes are sets of data that a Ressource Owner can share to a third-party application through this authorization process. List of scope is available here. You can also browse individual data and their scopes in the Data Catalog.
Type | Name | Value | Description | Required |
---|---|---|---|---|
Path Parameter | {brand.tld} |
{brand.tld} |
Vehicle manufacturer brand.tld. | Yes |
Query Parameter | client_id |
<client_id> |
Client id of your application. | Yes |
Query Parameter | response_type |
code |
Use OAuth2 Authorization code flow. | Yes |
Query Parameter | scope |
<scope1>,<scope2>,etc. |
The list of scopes requested to your customer (Ressource Owner). | Yes |
Query Parameter | redirect_uri |
<redirect_uri> |
The URI address to redirect the user at the end of the authorization process. | Yes |
Query Parameter | state |
<state> |
A state value generated on your side, it will be returned along with the Authorization Code in the redirection. Recommended, checking this parameter when it’s returned allows tracking and identifying the request, and prevent attacks. | No |
Query Parameter | local |
<local> |
End user browser language (by default, en-GB). Possible values: ar-EG,cs-CZ,da-DK,de-AT,de-CH,de-DE,en-AU,en-EG,en-GB,en-IE,en-JP,en-MT,en-NZ,en-ZA,es-AR,es-CL,es-ES,es-MX,et-EE,fi-FI,fr-BE,fr-CH,fr-DZ,fr-FR,fr-LU,fr-MA,fr-MQ,fr-RE,fr-TN,hr-HR,hu-HU,it-CH,it-IT,nb-NO,nl-BE,nl-NL,pl-PL,pt-BR,pt-PT,ru-RU,ru-UA,sk-SK,sl-SI,sv-SE,tr-TR,uk-UA,ar-DZ,ar-MA,ar-PS,el-GR,es-CO,es-PE,fa-IR,fr-GF,fr-GP,fr-MG,he-IL,ja-JP,ko-KR,en-PS,ar-SA,en-SA,ar-AE,en-AE,en-SG,es-UY,en-MY,en-BN,ro-RO,mk-MK,es-EC,en-ES,fr-LB,en-LB,bg-BG,sr-RS,en-CY,fr-NC,es-CR,es-PY,is-IS,en-IS,fr-SN,en-IN,en-JO,en-MU,lv-LV,lt-LT,zh-TW. | No |
/authorize
1
2
3
4
5
6
7
8
9
$ curl \
--request GET \
--url 'https://idpcvs.{brand.tld}/am/oauth2/authorize' \
--data-urlencode 'client_id=<client_id>' \
--data-urlencode 'response_type=code' \
--data-urlencode 'scope=vehicle_read,remote_write' \
--data-urlencode 'redirect_uri=<app_callback_uri>' \
--data-urlencode 'state=<state>' \
--data-urlencode 'local=<local>'
2️⃣•2️⃣ Prompt Triggering #
Before the Authorization prompt is triggered, Stellantis performs the following checks on the request:
- The App should be registered and the
should exists. - The requested scopes should match the one declared in the App Registration.
In cases these checks are performed successfully, the redirection prompt is sent to the End User device triggering an external user agent (web view) on the End User device.
2️⃣•3️⃣ User Inputs #
When the external user agent authorization prompt is triggered by the End User device, you have no more access to the authorization process.
In the prompted external user agent (web view), the user will follow the consent process to give access to its Stellantis Account.
The journey of the End User in the prompted external user agent is detailed in the User Journey section.
User Journey: if not previously performed for another Stellantis Connected service, the following steps are required on the End User side before being able to authorize access to its Stellantis Account:
- Create an account in Stellantis system.
- Link a vehicle to the account.
- Enable connectivity for partners (accessing party).
Closing the App: If the user needs to leave your application during the previous process, the authorization request might fail. In this case, the user needs to restart the authorization process from the start.
2️⃣•4️⃣ Authorization Code #
In the OAuth2 Authorization Code Flow, after the Resource Owner consent to share access to its data, an automatic redirection is triggered to the Accessing Party URI. This restriction includes an Authorization Code as an URI parameter. This Authorization Code can be exchanged against an Access Token.
If the account is set up properly (check out step 2.3), and the End-User authorizes your App to access the requested scopes, the redirection URI contains the Authorization Code.
The redirect URI will contain the following data as parameters:
- The list of scopes authorized by the user
- State value generated on your side from the initial request, for your own use.
- The authorization code, this code should be exchanged for an access token.
State: This state is for your own use. In order to mitigate CSRF attacks, you should make sure that the state value received in 2.3 is a state value that you have generated in step 2.1.
This state being generated on your side, you can also use it to embed data in step 2.1 for future use in step 2.3. Doing so, it’s possible to track the request and to notify your user if a consent request has been initialized but not completed.
/authorize
1
2
3
4
5
6
HTTP/1.1 302 Found
Date: Day, XX Mon YYYY HH:MM:SS GMT
Location: http://<app_callback_uri>
?scope=ConnectivityForPartners,Remote
&state=<state>
&code=<code>
3️⃣ Access Token #
The Authentication Code obtained in step 2 should be exchanged against an Access Token.
Client Secret: Exchanging the authentication code for an Access Token must be done from your own server and not from the Ressource Owner device. Furthermore the API used for this purpose requires your Application Client Secret and this data must remain confidential.
This API allows requesting access token, it requires the following parameters:
Type | Name | Value | Description | Required |
---|---|---|---|---|
Path Parameter | {brand.tld} |
{brand.tld} |
Vehicle manufacturer brand.tld. | Yes |
Header | authorization: Basic |
<base64(<client_id>:<client_secret>)> |
Your App Client Id and Secret encoded in base64. | Yes |
Header | content-type |
<application/x-www-form-urlencoded> |
Content type. | Yes |
Data | grant_type |
authorization_code |
Use OAuth2 Authorization code flow. | Yes |
Data | redirect_uri |
<redirect_uri> |
The URI redirection address. It must be the same as in step 2.1. | Yes |
Data | code |
<code> |
The authorization code received in step 2.4. | Yes |
/access_token
1
2
3
4
5
6
7
8
$ curl \
--request POST \
--url 'https://idpcvs.{brand.tld}/am/oauth2/access_token' \
--header 'authorization: Basic <base64(<client_id>:<client_secret>)>'\
--header 'content-type: application/x-www-form-urlencoded' \
--data 'grant_type=authorization_code' \
--data 'redirect_uri=<app_callback_uri>' \
--data 'code=<code>' \
1
2
3
4
5
6
7
8
9
10
11
HTTP/1.1 200 OK
Date: Day, XX Mon YYYY HH:MM:SS GMT
{
"scope": "openid profile",
"expires_in": <time>,
"token_type": "Bearer",
"refresh_token": <refresh_token>,
"id_token": <id_token>,
"access_token": <access_token>
}
Renew Token #
In case the access token expires, you should renew the set of tokens using the following API call from your server.
This request requires that you still have access to the refresh token of step 3.
Type | Name | Value | Description | Required |
---|---|---|---|---|
Path Parameter | {brand.tld} |
{brand.tld} |
Vehicle manufacturer brand.tld. | Yes |
Query Parameter | realm |
<realm> |
Vehicle manufacturer realm. | Yes |
Header | authorization: Basic |
<base64(<client_id>:<client_secret>)> |
Your App Client id and secret encoded in base64. | Yes |
Header | content-type |
<application/x-www-form-urlencoded> |
Content type. | Yes |
Data | grant_type |
refresh_token |
Token renewal. | Yes |
Data | refresh_token |
<refresh_token> |
Refresh token received in step 3. | Yes |
/access_token
1
2
3
4
5
6
7
8
$ curl \
--request POST \
--url 'https://idpcvs.{brand.tld}/am/oauth2/access_token' \
--data-urlencode 'realm=<realm>'
--header 'authorization: Basic <base64(<client_id>:<client_secret>)>'\
--header 'content-type: application/x-www-form-urlencoded' \
--data 'grant_type=refresh_token' \
--data 'refresh_token=<refresh_token>' \
1
2
3
4
5
6
7
8
9
10
11
HTTP/1.1 200 OK
Date: Day, XX Mon YYYY HH:MM:SS GMT
{
"scope": "openid profile",
"expires_in": <time>,
"token_type": "Bearer",
"refresh_token": <refresh_token>,
"id_token": <id_token>,
"access_token": <access_token>
}
Revoke Token (Logout User) #
Type | Name | Value | Description | Required |
---|---|---|---|---|
Path Parameter | {brand.tld} |
{brand.tld} |
Vehicle manufacturer brand.tld. | Yes |
Query Parameter | realm |
<realm> |
Vehicle manufacturer realm. | Yes |
Header | authorization: Basic |
<base64(<client_id>:<client_secret>)> |
Your App Client id and secret encoded in base64. | Yes |
Header | content-type |
<application/x-www-form-urlencoded> |
Content type. | Yes |
Data | token |
<refresh_toke OR access_token> |
Access or refresh token. | Yes |
You should implement a feature in your application if the End User wants to log out.
This action should lead to the revocation of the token:
/token/revoke
1
2
3
4
5
6
7
$ curl \
--request POST \
--url 'https://idpcvs.{brand.tld}/am/oauth2/token/revoke' \
--data-urlencode 'realm=<realm>' \
--header 'authorization: Basic <base64(<client_id>:<client_secret>)>'\
--header 'content-type: application/x-www-form-urlencoded' \
--data 'refresh_token=<refresh_toke OR access_token>' \
1
2
HTTP/1.1 200 OK
Date: Day, XX Mon YYYY HH:MM:SS GMT
Note: depending on your needs, it’s possible to revoke the access token or the renew token. Revoking a refresh token implies the revocation of all associated access token.
Access User Data
Once you have an Access Token, you can build the HTTP request to access this user data.