OAuth 2.0
Recommended authentication mechanism for Marketplace Applications
OAuth 2.0 is the recommended authentication method for all Marketplace apps. It provides a secure and user-friendly way to access Samsara's API by allowing your users to grant permissions through a simple approval flow, eliminating the need to share API tokens manually.
For direct integrations, consider using API tokens created from your Samsara dashboard. Use OAuth if enhanced security with token refresh is needed, but note it requires more development effort. Learn more about using API tokens for authentication.
Review end-to-end examples on GitHub.
Setting Up Your OAuth 2.0 App
From the Samsara dashboard select Settings > OAuth 2.0 Apps
To create a new OAuth 2.0 App, you'll need the following:
Field | Description | Required |
---|---|---|
App name | Your app's name, which appears on the Samsara dashboard. | Yes |
Redirect URL | The URL where users are redirected after granting access. | Yes |
Scopes | The permissions your App requires. Each API endpoint requires specific scopes, which you can find in the API Reference. | Yes |
App Logo | A JPEG or PNG. Suggested dimensions are 75x75 (max size 100KB). | Yes |
Direct Install URL | A URL in your app where users can initiate the OAuth 2.0 flow. Enables installation from the Samsara Dashboard. | No |
Warning: Scope changes affect ALL app installs
Scope changes apply to all future app installs or updates. Users must re-authorize or update the app for updated scopes.
Record your App ID and App Secret credentials, as they are used to authenticate calls related to the OAuth process:
- App ID (also known as
client_id
) - App Secret (also known as
client_secret
)
Store your App Secret securely
Your App Secret is a form of credential like a password. To keep your integration secure, never store it in your source code or commit it to version control. Instead, read the App Secret from an environment variable or secret manager.
As an example, your .env
file might look like this:
# .env
SAMSARA_CLIENT_ID=<your_client_id>
SAMSARA_CLIENT_SECRET=<your_client_secret>
Implementation
Samsara follows the OAuth 2.0 authorization code grant flow.
Overview
- Step 1: Redirect users to
/oauth2/authorize
to authorize access - Step 2: Users are redirected to your
redirect_uri
with acode
- Step 3: Make an API call to
/oauth2/token
to exchange thecode
for a set of credentials includingaccess_token
andrefresh_token
- Step 4: Use the user's
access_token
as a Bearer token to make API calls on their behalf - Step 5: If the credentials are expired when attempting to make an API call, refresh tokens first using the
/oauth2/token
endpoint, then make the API call
Step 1: Authorization Request
Start the OAuth 2.0 flow by redirecting users to the authorize endpoint /oauth2/authorize
with these query parameters:
Parameter | Description | Required |
---|---|---|
client_id | App ID provided when you created your OAuth 2.0 App in the Samsara Dashboard. | Yes |
state | A unique code to prevent CSRF attacks. Verify this code in Step 2 when the user is redirected. Must be more than 8 characters. | Yes |
response_type | Must be code as only authorization code grant flow is currently supported. | Yes |
redirect_uri | The URI of your app that Samsara will redirect users back to after they grant access. Only required if you support multiple redirect URIs. Must use SSL protocol (https). | Optional |
Example
Here's an example link with the client_id
, state
, and response_type
parameters:
<a href="https://api.samsara.com/oauth2/authorize?client_id=IEC65XwwV9&state=z3qAr0h5Ud&response_type=code">Connect to Samsara</a>
When a user clicks the authorize link, they'll be redirected to the Samsara authorization page where they can grant access to your app.
Step 2: Handle Authorization Response
When the user clicks "Allow", Samsara redirects them to your redirect_uri
with these query parameters:
Parameter | Description |
---|---|
code | Authorization code to exchange for an access token. Expires after 10 minutes. |
state | Same as the state parameter from Step 1. If they differ, abort the flow as this may indicate a CSRF attack. |
scope | The access level granted to your app. admin:read for read-only access or admin:write for full read/write access. |
Example
When a user grants access, Samsara redirects them to your app with a URL like this:
https://my-app.com?code=hgg2tziN39&state=z3qAr0h5Ud&scope=admin%3Aread
If a user clicks "Cancel" or if an error occurs, they are redirected to the redirect_uri
with the following query parameters:
Parameter | Description |
---|---|
error | The error code. Common values include scope_not_granted and invalid_request . |
error_description | A human-readable description of the error. |
error_hint | A human-readable hint that may assist the client in resolving the error. |
state | The state parameter from Step 1. |
Example
When a user denies access, the redirect URL includes error details like this:
https://my-app.com?error=scope_not_granted&error_description=The+token+was+not+granted+the+requested+scope&error_hint=The+resource+owner+did+not+grant+the+requested+scope.&state=PDoPp3AE-4AqDHP4dXnbeA
Step 3: Token Exchange
Exchange the authorization code
for API credentials with a POST
request to the /oauth2/token
endpoint.
Store the access_token
and refresh_token
in your database, associating them with the relevant user, organization, or account.
Calculate and save an expires_at
timestamp using the expires_in
value from the response. Before making an API call, check if the token has expired; if so, refresh it just-in-time.
Note: You don't need to refresh credentials before expiration if it is not being used to make an API call.
Authorization
To authenticate using HTTP Basic Auth using your OAuth 2.0 client ID and client secret:
- Combine the
client_id
andclient_secret
with a colon (e.g."client_id:client_secret"
) - Base64 encode that string
- Add
"Basic "
followed by the encoded string to the Authorization header
For example, if your credentials are:
- client_id:
my_id
- client_secret:
my_secret
The Authorization header would be: Authorization: Basic bXlfaWQ6bXlfc2VjcmV0
const clientId = process.env.SAMSARA_OAUTH_CLIENT_ID;
const clientSecret = process.env.SAMSARA_OAUTH_CLIENT_SECRET;
// Base64 encoded authorization string
const auth = Buffer.from(`${clientId}:${clientSecret}`).toString("base64");
// Make the request with header `Authorization: Basic ${auth}`
client_id = os.getenv("SAMSARA_OAUTH_CLIENT_ID")
client_secret = os.getenv("SAMSARA_OAUTH_CLIENT_SECRET")
# Base64 encoded authorization string
auth = base64.b64encode(f"{client_id}:{client_secret}".encode()).decode()
# Make the request with header: `Authorization: Basic ${auth}`
# Note: The `requests` library supports Basic Auth by passing a tuple of (client_id, client_secret) to the `auth` parameter
# E.g.
# requests.post(
# 'https://api.samsara.com/oauth2/token',
# auth=(SAMSARA_CLIENT_ID, SAMSARA_CLIENT_SECRET),
# data={'grant_type': 'refresh_token', 'refresh_token': token })
client_id = ENV['SAMSARA_CLIENT_ID']
client_secret = ENV['SAMSARA_CLIENT_SECRET']
auth = Base64.strict_encode64("#{client_id}:#{client_secret}")
var clientId = Environment.GetEnvironmentVariable("SAMSARA_CLIENT_ID");
var clientSecret = Environment.GetEnvironmentVariable("SAMSARA_CLIENT_SECRET");
var auth = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes($"{clientId}:{clientSecret}"));
// using var client = new HttpClient();
// client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", auth);
// Add basic auth header
clientId := os.Getenv("SAMSARA_CLIENT_ID")
clientSecret := os.Getenv("SAMSARA_CLIENT_SECRET")
combined := clientId + ":" + clientSecret
auth := base64.StdEncoding.EncodeToString([]byte(combined))
// req.Header.Add("Authorization", "Basic "+auth)
String clientId = System.getenv("SAMSARA_CLIENT_ID");
String clientSecret = System.getenv("SAMSARA_CLIENT_SECRET");
String combinedSecrets = clientId + ":" + clientSecret;
String auth = Base64.getEncoder().encodeToString(combinedSecrets.getBytes());
// HttpRequest request =
// HttpRequest.newBuilder()
// .header("Authorization", "Basic " + auth)
// .build();
// Create authorization header
$combined = $_ENV['SAMSARA_CLIENT_ID'] . ':' . $_ENV['SAMSARA_CLIENT_SECRET'];
$auth = base64_encode($combined);
// $ch = curl_init();
// curl_setopt($ch, CURLOPT_HTTPHEADER, [
// 'Authorization: Basic ' . $encoded_auth,
// ]);
Body
- Content-Type:
application/x-www-form-urlencoded
Parameter | Description | Required |
---|---|---|
code | The verification code provided to you in Step 2. | Yes |
grant_type | Must be authorization_code . | Yes |
Response
The API returns a JSON response containing the following:
{
"access_token": "hXdwQUq3GBKZTvSLyURh",
"expires_in": 3599,
"scope": "admin:read",
"token_type": "bearer",
"refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA"
}
Field | Description |
---|---|
access_token | The token to authenticate with the Samsara API on behalf of the user. |
expires_in | The number of seconds until the access token expires. Access tokens expire after 1 hour. After the access token expires, you must request a new one using the refresh token. See Refresh Tokens. |
scope | admin:read for read-only apps or admin:write for full-access read/write apps. |
token_type | bearer indicating the access token is to be used with the Authorization: Bearer HTTP header for calls to the Samsara API. |
refresh_token | The token used to request a new access token when the access token expires. See Refresh Tokens. |
Once an authorization
code
is exchanged for a token, the code will no longer be valid.
Example
How to exchange an authorization code
for a API credentials:
POST /oauth2/token HTTP/1.1
Host: api.samsara.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic bXlfaWQ6bXlfc2VjcmV0
code=hgg2tziN39&grant_type=authorization_code
curl -X POST \
https://api.samsara.com/oauth2/token \
-H 'Authorization: Basic bXlfaWQ6bXlfc2VjcmV0' \
-d 'code=hgg2tziN39&grant_type=authorization_code'
// Using node-fetch
const fetch = (...args) =>
import("node-fetch").then(({ default: fetch }) => fetch(...args));
const clientId = process.env.SAMSARA_CLIENT_ID;
const clientSecret = process.env.SAMSARA_CLIENT_SECRET;
// Base64 encoded authorization string
const auth = Buffer.from(`${clientId}:${clientSecret}`).toString("base64");
const response = await fetch("https://api.samsara.com/oauth2/token", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
Authorization: `Basic ${auth}`,
},
body: new URLSearchParams({
grant_type: "authorization_code",
code: code, // from query string
}),
});
const {access_token, refresh_token, expires_in} = await response.json();
// Calculate expire timestamp
const expiresAt = Math.floor(Date.now() / 1000) + expires_in;
// Store in session
const credentials = {
access_token: access_token,
refresh_token: refresh_token,
expires_at: expiresAt,
};
SAMSARA_CLIENT_ID = os.getenv('SAMSARA_CLIENT_ID')
SAMSARA_CLIENT_SECRET = os.getenv('SAMSARA_CLIENT_SECRET')
# Within your redirect handler:
response = requests.post(
"https://api.samsara.com/oauth2/token",
data={
'code': code, # from query string
'grant_type': 'authorization_code',
},
auth=(SAMSARA_CLIENT_ID, SAMSARA_CLIENT_SECRET)
)
token_data = response.json()
access_token = token_data.get('access_token')
refresh_token = token_data.get('refresh_token')
expires_at = time.time() + token_data.get('expires_in')
# Store the tokens in the session
credentials = {
'access_token': access_token,
'refresh_token': refresh_token,
'expires_at': expires_at
}
print(credentials)
auth = Base64.strict_encode64("#{ENV['SAMSARA_CLIENT_ID']}:#{ENV['SAMSARA_CLIENT_SECRET']}")
uri = URI('https://api.samsara.com/oauth2/token')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Content-Type'] = 'application/x-www-form-urlencoded'
request['Authorization'] = "Basic #{auth}"
request.body = URI.encode_www_form({
code: params[:code], # from query string
grant_type: 'authorization_code',
})
response = http.request(request)
if response.code == '200'
token_data = JSON.parse(response.body)
access_token = token_data['access_token']
refresh_token = token_data['refresh_token']
expires_in = token_data['expires_in']
# Calculate expires_at timestamp
expires_at = Time.now.to_i + expires_in
credentials = {
'access_token' => access_token,
'refresh_token' => refresh_token,
'expires_at' => expires_at
}
end
using var client = new HttpClient();
var tokenRequest = new FormUrlEncodedContent(new Dictionary<string, string>
{
["grant_type"] = "authorization_code",
["code"] = code // from query string
});
// Add Basic Auth header
var clientId = Environment.GetEnvironmentVariable("SAMSARA_CLIENT_ID");
var clientSecret = Environment.GetEnvironmentVariable("SAMSARA_CLIENT_SECRET");
var auth = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes($"{clientId}:{clientSecret}"));
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", auth);
var response = await client.PostAsync("https://api.samsara.com/oauth2/token", tokenRequest);
var result = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
context.Response.StatusCode = 400;
await context.Response.WriteAsync($"Failed to exchange code for tokens: {result}");
return;
}
// Parse token response using JsonElement to handle numeric values correctly
var tokenResponse = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(result);
var accessToken = tokenResponse["access_token"].GetString();
var refreshToken = tokenResponse["refresh_token"].GetString();
var expiresIn = tokenResponse["expires_in"].GetInt32();
// Calculate when the token will expire
var expiresAt = DateTimeOffset.UtcNow.AddSeconds(expiresIn).ToUnixTimeSeconds();
var credentials = new Dictionary<string, string>
{
["access_token"] = accessToken,
["refresh_token"] = refreshToken,
["expires_at"] = expiresAt.ToString()
};
client := &http.Client{}
tokenURL := "https://api.samsara.com/oauth2/token"
data := url.Values{}
data.Set("code", code)
data.Set("grant_type", "authorization_code")
req, err := http.NewRequest("POST", tokenURL, strings.NewReader(data.Encode()))
if err != nil {
http.Error(w, "Error creating token request", http.StatusInternalServerError)
return
}
// Add headers
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
// Add basic auth header
auth := os.Getenv("SAMSARA_CLIENT_ID") + ":" + os.Getenv("SAMSARA_CLIENT_SECRET")
basicAuth := base64.StdEncoding.EncodeToString([]byte(auth))
req.Header.Add("Authorization", "Basic "+basicAuth)
// Make request
resp, err := client.Do(req)
if err != nil {
http.Error(w, "Error exchanging code for token", http.StatusInternalServerError)
return
}
defer resp.Body.Close()
// Parse response
var result struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
ExpiresIn int `json:"expires_in"`
TokenType string `json:"token_type"`
Scope string `json:"scope"`
}
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
http.Error(w, "Error parsing token response", http.StatusInternalServerError)
return
}
// Store tokens in session
credentials := map[string]interface{}{
"access_token": result.AccessToken,
"refresh_token": result.RefreshToken,
"expires_at": time.Now().Unix(), // + int64(result.ExpiresIn),
}
HttpClient client = HttpClient.newHttpClient();
String code = "<code from query string>";
String requestBody =
String.format("grant_type=authorization_code&code=%s", code);
String clientId = System.getenv("SAMSARA_CLIENT_ID");
String clientSecret = System.getenv("SAMSARA_CLIENT_SECRET");
String auth = Base64.getEncoder().encodeToString(
(clientId + ":" + clientSecret).getBytes());
HttpRequest request =
HttpRequest.newBuilder()
.uri(URI.create("https://api.samsara.com/oauth2/token"))
.header("Content-Type", "application/x-www-form-urlencoded")
.header("Authorization", "Basic " + auth)
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
.build();
HttpResponse<String> response =
client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() != 200) {
res.status(400);
return "Failed to exchange code for tokens";
}
JsonObject tokens =
JsonParser.parseString(response.body()).getAsJsonObject();
String accessToken = tokens.get("access_token").getAsString();
String refreshToken = tokens.get("refresh_token").getAsString();
long expiresIn = tokens.get("expires_in").getAsLong();
long expiresAt = System.currentTimeMillis() / 1000 + expiresIn;
<?php
// Extract code and state from query string
$code = $_GET['code'] ?? null;
// Create authorization header
$auth = $_ENV['SAMSARA_CLIENT_ID'] . ':' . $_ENV['SAMSARA_CLIENT_SECRET'];
$encoded_auth = base64_encode($auth);
// Initialize cURL session
$ch = curl_init();
// Set cURL options for token exchange
curl_setopt($ch, CURLOPT_URL, 'https://api.samsara.com/oauth2/token');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Basic ' . $encoded_auth,
'Content-Type: application/x-www-form-urlencoded'
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
'grant_type' => 'authorization_code',
'code' => $code,
]));
// Execute request
$response = curl_exec($ch);
// Check for cURL errors
if (curl_errno($ch)) {
die('Error exchanging code for tokens: ' . curl_error($ch));
}
// Close cURL session
curl_close($ch);
// Parse response
$token_data = json_decode($response, true);
// Calculate expires_at timestamp
$expires_at = time() + $token_data['expires_in'];
// Store credentials in session
$credentials = [
'access_token' => $token_data['access_token'],
'refresh_token' => $token_data['refresh_token'],
'expires_at' => $expires_at
];
Step 4: Using the Access Token
Authenticate to the API by providing the access token in the Authorization: Bearer
HTTP header.
Authorization: Bearer <access_token>
Access tokens last for 1 hour (3600 seconds), matching the expires_in
value you receive. To request a new one see Refresh an Expired Access Token.
As an example, if the token is hXdwQUq3GBKZTvSLyURh
, this is how to make a request to the /fleet/vehicles
endpoint:
curl -X GET \
https://api.samsara.com/fleet/vehicles \
-H 'Authorization: Bearer hXdwQUq3GBKZTvSLyURh'
const accessToken = 'hXdwQUq3GBKZTvSLyURh';
// Using node-fetch
const fetch = (...args) =>
import("node-fetch").then(({ default: fetch }) => fetch(...args));
fetch('https://api.samsara.com/fleet/vehicles', {
method: 'GET',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json',
}
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
access_token = 'hXdwQUq3GBKZTvSLyURh'
headers = {
'Authorization': f'Bearer {access_token}',
'Content-Type': 'application/json'
}
response = requests.get('https://api.samsara.com/fleet/vehicles', headers=headers)
response.raise_for_status()
vehicles = response.json()
print(vehicles)
access_token = 'hXdwQUq3GBKZTvSLyURh'
uri = URI('https://api.samsara.com/fleet/vehicles')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri)
request['Authorization'] = "Bearer #{access_token}"
response = http.request(request)
p response
var accessToken = "hXdwQUq3GBKZTvSLyURh";
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken);
var response = await client.GetAsync("https://api.samsara.com/fleet/vehicles");
var rawResponse = await response.Content.ReadAsStringAsync();
accessToken := "hXdwQUq3GBKZTvSLyURh"
client := &http.Client{}
req, err := http.NewRequest("GET", "https://api.samsara.com/fleet/vehicles", nil)
if err != nil {
http.Error(w, "Error creating request", http.StatusInternalServerError)
return
}
// Add headers
req.Header.Add("Authorization", "Bearer "+accessToken)
req.Header.Add("Content-Type", "application/json")
// Make request
resp, err := client.Do(req)
if err != nil {
http.Error(w, "Error making request", http.StatusInternalServerError)
return
}
defer resp.Body.Close()
fmt.Println(resp.Body)
String accessToken = "hXdwQUq3GBKZTvSLyURh";
HttpClient client = HttpClient.newHttpClient();
HttpRequest request =
HttpRequest.newBuilder()
.uri(URI.create("https://api.samsara.com/fleet/vehicles"))
.header("Authorization", "Bearer " + accessToken)
.GET()
.build();
HttpResponse<String> response =
client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() != 200) {
return "API call failed with status code: " + response.statusCode();
}
System.out.println(response.body());
<?php
$access_token = 'hXdwQUq3GBKZTvSLyURh';
$ch = curl_init();
// Set cURL options
curl_setopt($ch, CURLOPT_URL, 'https://api.samsara.com/fleet/vehicles');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $access_token,
'Accept: application/json'
]);
// Execute request
$response = curl_exec($ch);
// Check for errors
if (curl_errno($ch)) {
die('Error making API request: ' . curl_error($ch));
}
// Get HTTP status code
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
// Close cURL session
curl_close($ch);
// Set JSON content type header
header('Content-Type: application/json');
// Check if request was successful
if ($http_code !== 200) {
die(json_encode([
'error' => 'API request failed',
'status' => $http_code,
'response' => $response
]));
}
// Output response
echo $response;
Step 5: Refresh an Expired Access Token
After an access token expires, you can request a new one using the refresh_token
provided in Step 3. Make a POST
request to the /oauth2/token
endpoint with the following parameters:
Parameter | Description | Required |
---|---|---|
refresh_token | The refresh token provided in Step 3. | Yes |
grant_type | Must be refresh_token . | Yes |
Warning against concurrent OAuth refresh requests
Do not make concurrent requests to the
/oauth2/token
endpoint to refresh the same API token.You must only make one request at a time with the same refresh token, otherwise your integration may break. Wait until receiving a response before retrying.
- refresh tokens may only be used once, then they expire
- we recommend conservative retry logic: you should receive a response from Samsara in milliseconds, however the upper bound (p99) can be up to 20 seconds due to server load and processing times
Note that you may make concurrent requests to the other Samsara endpoints using the same access token (e.g. to
/fleet/drivers
or/fleet/vehicles
, etc.).
Authorization
To authenticate using HTTP Basic Auth using your OAuth 2.0 client ID and client secret:
- Combine the
client_id
andclient_secret
with a colon (e.g."client_id:client_secret"
) - Base64 encode that string
- Add
"Basic "
followed by the encoded string to the Authorization header
For example, if your credentials are:
- client_id:
my_id
- client_secret:
my_secret
The Authorization header would be: Authorization: Basic bXlfaWQ6bXlfc2VjcmV0
const clientId = process.env.SAMSARA_OAUTH_CLIENT_ID;
const clientSecret = process.env.SAMSARA_OAUTH_CLIENT_SECRET;
// Base64 encoded authorization string
const auth = Buffer.from(`${clientId}:${clientSecret}`).toString("base64");
// Make the request with header `Authorization: Basic ${auth}`
client_id = os.getenv("SAMSARA_OAUTH_CLIENT_ID")
client_secret = os.getenv("SAMSARA_OAUTH_CLIENT_SECRET")
# Base64 encoded authorization string
auth = base64.b64encode(f"{client_id}:{client_secret}".encode()).decode()
# Make the request with header: `Authorization: Basic ${auth}`
# Note: The `requests` library supports Basic Auth by passing a tuple of (client_id, client_secret) to the `auth` parameter
# E.g.
# requests.post(
# 'https://api.samsara.com/oauth2/token',
# auth=(SAMSARA_CLIENT_ID, SAMSARA_CLIENT_SECRET),
# data={'grant_type': 'refresh_token', 'refresh_token': token })
client_id = ENV['SAMSARA_CLIENT_ID']
client_secret = ENV['SAMSARA_CLIENT_SECRET']
auth = Base64.strict_encode64("#{client_id}:#{client_secret}")
var clientId = Environment.GetEnvironmentVariable("SAMSARA_CLIENT_ID");
var clientSecret = Environment.GetEnvironmentVariable("SAMSARA_CLIENT_SECRET");
var auth = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes($"{clientId}:{clientSecret}"));
// using var client = new HttpClient();
// client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", auth);
// Add basic auth header
clientId := os.Getenv("SAMSARA_CLIENT_ID")
clientSecret := os.Getenv("SAMSARA_CLIENT_SECRET")
combined := clientId + ":" + clientSecret
auth := base64.StdEncoding.EncodeToString([]byte(combined))
// req.Header.Add("Authorization", "Basic "+auth)
String clientId = System.getenv("SAMSARA_CLIENT_ID");
String clientSecret = System.getenv("SAMSARA_CLIENT_SECRET");
String combinedSecrets = clientId + ":" + clientSecret;
String auth = Base64.getEncoder().encodeToString(combinedSecrets.getBytes());
// HttpRequest request =
// HttpRequest.newBuilder()
// .header("Authorization", "Basic " + auth)
// .build();
// Create authorization header
$combined = $_ENV['SAMSARA_CLIENT_ID'] . ':' . $_ENV['SAMSARA_CLIENT_SECRET'];
$auth = base64_encode($combined);
// $ch = curl_init();
// curl_setopt($ch, CURLOPT_HTTPHEADER, [
// 'Authorization: Basic ' . $encoded_auth,
// ]);
Body
- Content-Type:
application/x-www-form-urlencoded
Parameter | Description | Required |
---|---|---|
refresh_token | The refresh token provided to you in Step 3. | Yes |
grant_type | Must be refresh_token . | Yes |
Response
You'll receive a JSON response containing the following:
{
"access_token": "2YotnFZFEjr1zCsicMWpAA",
"refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA",
"expires_in": 3599,
"scope": "admin:read",
"token_type": "bearer"
}
Field | Description |
---|---|
access_token | The new access token to authenticate with the Samsara API. |
refresh_token | The new refresh token for the next time the access token expires. |
expires_in | The number of seconds before the new access token expires. After the access token expires, you must request a new one using the new refresh token. |
scope | admin:read for read-only apps or admin:write for full-access read/write apps. |
token_type | Indicates that the access token is to be used with the Authorization: Bearer HTTP header in calls to the Samsara API. |
Each refresh token can only be used once. Update both your access token and refresh token after each refresh.
Failed Refresh If the new refreshed credentials are not stored and the refresh token was used you can ask the customer to re-authenticate starting at Step 1.
Additional Features
Direct Installation
Users can start the installation flow in two ways:
- From your application
- From the Samsara dashboard where they are linked to your application (Direct Install URL).
Set a Direct Install URL during app registration to let users start the OAuth flow directly from the Samsara dashboard. This URL should point to where users begin Step 1 of the OAuth process.
Example
A Direct Install URL that redirects to a landing page on your website: https://my-app.com/oauth/start?source=samsara
. You can include additional query parameters as needed for your application.
Revoking Access
Users can revoke authorization from your app through the Samsara Apps page in the Samsara dashboard. This action invalidates all access tokens and refresh tokens for that organization. Requests made with invalid tokens will return responses with 400 status code with the following message: "The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client."
When a user wants to revoke access to your app, you can make a POST
request to the /oauth2/revoke
endpoint to invalidate their tokens:
curl -X POST \
https://api.samsara.com/oauth2/revoke \
-H 'Authorization: Basic bXlfaWQ6bXlfc2VjcmV0' \
-d 'token=tGzv3JOkF0XG5Qx2TlKWIA'
const clientId = process.env.SAMSARA_CLIENT_ID;
const clientSecret = process.env.SAMSARA_CLIENT_SECRET;
const refreshToken = 'tGzv3JOkF0XG5Qx2TlKWIA';
const auth = Buffer.from(`${clientId}:${clientSecret}`).toString("base64");
fetch('https://api.samsara.com/oauth2/revoke', {
method: 'POST',
headers: {
'Authorization': `Basic ${auth}`
},
body: `token=${refreshToken}`
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
client_id = os.getenv("SAMSARA_CLIENT_ID")
client_secret = os.getenv("SAMSARA_CLIENT_SECRET")
refresh_token = 'tGzv3JOkF0XG5Qx2TlKWIA'
# Within your revoke handler:
response = requests.post(
"https://api.samsara.com/oauth2/revoke",
data={
'token': refresh_token,
},
auth=(client_id, client_secret)
)
print(response.json())
client_id = ENV['SAMSARA_CLIENT_ID']
client_secret = ENV['SAMSARA_CLIENT_SECRET']
auth = Base64.strict_encode64("#{client_id}:#{client_secret}")
refresh_token = 'tGzv3JOkF0XG5Qx2TlKWIA'
uri = URI('https://api.samsara.com/oauth2/revoke')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Content-Type'] = 'application/x-www-form-urlencoded'
request['Authorization'] = "Basic #{auth}"
request.body = URI.encode_www_form({
token: refresh_token
})
response = http.request(request)
var clientId = Environment.GetEnvironmentVariable("SAMSARA_CLIENT_ID");
var clientSecret = Environment.GetEnvironmentVariable("SAMSARA_CLIENT_SECRET");
var refreshToken = "tGzv3JOkF0XG5Qx2TlKWIA";
// Call Samsara revoke endpoint
using var client = new HttpClient();
var revokeEndpoint = "https://api.samsara.com/oauth2/revoke";
// Add basic auth header
var auth = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes($"{clientId}:{clientSecret}"));
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", auth);
var revokeRequest = new Dictionary<string, string>
{
{ "token", refreshToken }
};
var revokeResponse = await client.PostAsync(revokeEndpoint, new FormUrlEncodedContent(revokeRequest));
var revokeResponseContent = await revokeResponse.Content.ReadAsStringAsync();
Console.WriteLine(revokeResponseContent);
clientId := os.Getenv("SAMSARA_CLIENT_ID")
clientSecret := os.Getenv("SAMSARA_CLIENT_SECRET")
refreshToken := "tGzv3JOkF0XG5Qx2TlKWIA"
// Create auth header
auth := clientId + ":" + clientSecret
basicAuth := base64.StdEncoding.EncodeToString([]byte(auth))
// Create request with refresh token in body
client := &http.Client{}
data := url.Values{}
data.Set("token", refreshToken)
req, err := http.NewRequest("POST", "https://api.samsara.com/oauth2/revoke", strings.NewReader(data.Encode()))
if err != nil {
http.Error(w, "Error creating request", http.StatusInternalServerError)
return
}
// Add headers
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
req.Header.Add("Authorization", "Basic "+basicAuth)
// Make request
resp, err := client.Do(req)
if err != nil {
http.Error(w, "Error making request", http.StatusInternalServerError)
return
}
defer resp.Body.Close()
String clientId = System.getenv("SAMSARA_CLIENT_ID");
String clientSecret = System.getenv("SAMSARA_CLIENT_SECRET");
String refreshToken = "tGzv3JOkF0XG5Qx2TlKWIA";
HttpClient client = HttpClient.newHttpClient();
String auth = Base64.getEncoder().encodeToString(
(clientId + ":" + clientSecret).getBytes());
// Build request body
String requestBody = "token=" + refreshToken;
// Create request
HttpRequest request =
HttpRequest.newBuilder()
.uri(URI.create("https://api.samsara.com/oauth2/revoke"))
.header("Content-Type", "application/x-www-form-urlencoded")
.header("Authorization", "Basic " + auth)
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
.build();
HttpResponse<String> response =
client.send(request, HttpResponse.BodyHandlers.ofString());
<?php
$refresh_token = 'tGzv3JOkF0XG5Qx2TlKWIA';
// Create authorization header
$auth = $_ENV['SAMSARA_CLIENT_ID'] . ':' . $_ENV['SAMSARA_CLIENT_SECRET'];
$encoded_auth = base64_encode($auth);
// Initialize cURL session
$ch = curl_init();
// Set cURL options
curl_setopt($ch, CURLOPT_URL, 'https://api.samsara.com/oauth2/revoke');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Basic ' . $encoded_auth,
'Content-Type: application/x-www-form-urlencoded'
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
'token' => $refresh_token
]));
// Execute request
$response = curl_exec($ch);
// Get HTTP status code
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
// Check for errors
if (curl_errno($ch)) {
die('Error revoking token: ' . curl_error($ch));
}
// Close cURL session
curl_close($ch);
Authorization
To authenticate using HTTP Basic Auth using your OAuth 2.0 client ID and client secret:
- Combine the
client_id
andclient_secret
with a colon (e.g."client_id:client_secret"
) - Base64 encode that string
- Add
"Basic "
followed by the encoded string to the Authorization header
For example, if your credentials are:
- client_id:
my_id
- client_secret:
my_secret
The Authorization header would be: Authorization: Basic bXlfaWQ6bXlfc2VjcmV0
const clientId = process.env.SAMSARA_OAUTH_CLIENT_ID;
const clientSecret = process.env.SAMSARA_OAUTH_CLIENT_SECRET;
// Base64 encoded authorization string
const auth = Buffer.from(`${clientId}:${clientSecret}`).toString("base64");
// Make the request with header `Authorization: Basic ${auth}`
client_id = os.getenv("SAMSARA_OAUTH_CLIENT_ID")
client_secret = os.getenv("SAMSARA_OAUTH_CLIENT_SECRET")
# Base64 encoded authorization string
auth = base64.b64encode(f"{client_id}:{client_secret}".encode()).decode()
# Make the request with header: `Authorization: Basic ${auth}`
# Note: The `requests` library supports Basic Auth by passing a tuple of (client_id, client_secret) to the `auth` parameter
# E.g.
# requests.post(
# 'https://api.samsara.com/oauth2/token',
# auth=(SAMSARA_CLIENT_ID, SAMSARA_CLIENT_SECRET),
# data={'grant_type': 'refresh_token', 'refresh_token': token })
client_id = ENV['SAMSARA_CLIENT_ID']
client_secret = ENV['SAMSARA_CLIENT_SECRET']
auth = Base64.strict_encode64("#{client_id}:#{client_secret}")
var clientId = Environment.GetEnvironmentVariable("SAMSARA_CLIENT_ID");
var clientSecret = Environment.GetEnvironmentVariable("SAMSARA_CLIENT_SECRET");
var auth = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes($"{clientId}:{clientSecret}"));
// using var client = new HttpClient();
// client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", auth);
// Add basic auth header
clientId := os.Getenv("SAMSARA_CLIENT_ID")
clientSecret := os.Getenv("SAMSARA_CLIENT_SECRET")
combined := clientId + ":" + clientSecret
auth := base64.StdEncoding.EncodeToString([]byte(combined))
// req.Header.Add("Authorization", "Basic "+auth)
String clientId = System.getenv("SAMSARA_CLIENT_ID");
String clientSecret = System.getenv("SAMSARA_CLIENT_SECRET");
String combinedSecrets = clientId + ":" + clientSecret;
String auth = Base64.getEncoder().encodeToString(combinedSecrets.getBytes());
// HttpRequest request =
// HttpRequest.newBuilder()
// .header("Authorization", "Basic " + auth)
// .build();
// Create authorization header
$combined = $_ENV['SAMSARA_CLIENT_ID'] . ':' . $_ENV['SAMSARA_CLIENT_SECRET'];
$auth = base64_encode($combined);
// $ch = curl_init();
// curl_setopt($ch, CURLOPT_HTTPHEADER, [
// 'Authorization: Basic ' . $encoded_auth,
// ]);
Body
- Content-Type:
application/x-www-form-urlencoded
Parameter | Description | Required |
---|---|---|
token | The refresh token for the organization requesting revocation of app authorization. The access token will also be revoked. | Yes |
Response
You'll receive a 200 HTTP status code if the token is successfully revoked.
Publishing to App Marketplace
After testing your integration with multiple customers and ensuring it works reliably, review the requirements to get your app certified for the Samsara App Marketplace.
Updated 1 day ago