Missing org_id in Auth0 Device Authentication Flow
The Device Authentication Flow currently does not natively support the Organizations feature. The standard org_id claim is not automatically populated in the access token, nor can it be directly inserted within a Post-Login Action. This article provides two workarounds to associate an organisation ID with a token issued by this flow.
- Device Authorization Flow
- Organizations
- Actions (Post-Login and Token Exchange)
It is not possible to directly overwrite or insert the standard org_id claim using a Post-Login Action, as this is designated as a restricted claim by Auth0.
Depending on the application flexibility and the subscription plan, select one of the following approaches to associate an organization ID with tokens.
Option 1
Insert Organization via a Custom Claim (Post-Login Action)
This approach requires downstream applications to read a custom claim instead of the standard org_id claim.
-
Determine the user's organization using a Post-Login Action.
-
Insert the resulting organization ID into a namespaced custom claim (for example,
https://<domain.com>/org_id).
NOTE: If the user is a member of multiple organizations, the Action logic requires specific rules to determine which organization ID to append.
Option 2
Populate Standard org_id via Token Exchange
This approach utilizes the Token Exchange Flow for applications that strictly require the standard org_id claim.
NOTE: This feature is available for B2B Professional and above (Enterprise) plans.
This approach is a two-step process that relies on Approach 1 to pass the necessary data to the Token Exchange trigger:
- Create a Post-Login Action to inject a custom claim. During the initial Device Flow, this Action captures the user's organization and attaches it to the access token as a custom claim.
/**
* Handler that will be called during the execution of a PostLogin flow.
*
* @param {Event} event - Details about the user and the context in which they are logging in.
* @param {PostLoginAPI} api - Interface whose methods can be used to change the behavior of the login.
*/
exports.onExecutePostLogin = async (event, api) => {
// 1. Ensure this logic only runs for the Device Authorization Flow
if (event.transaction && event.transaction.protocol === 'oauth2-device-code') {
// 2. Define your custom namespace
const NAMESPACE = 'https://your-domain.com';
// 3. Determine the target Organization ID.
// NOTE: This logic depends on your specific use case. You might read this
// from user metadata (shown below) or use the Management API to query their orgs.
const targetOrgId = event.user.app_metadata?.default_org_id;
// 4. Inject the custom claim into the Access Token
if (targetOrgId) {
api.accessToken.setCustomClaim(`${NAMESPACE}/org_id`, targetOrgId);
console.log(`Injected custom org_id claim: ${targetOrgId}`);
} else {
console.log("No organization ID found for this user.");
}
}
};
- Create a Custom Token Exchange Action. The application uses the access token generated in Step 1 to initiate a Token Exchange Flow. This Action intercepts the request, reads the custom claim, and uses
api.authentication.setOrganization()to issue a new token featuring the standardorg_idclaim.
/**
* Handler that will be called during the execution of a Custom Token Exchange.
*
* @param {Event} event - Details about the client request.
* @param {TokenExchangeAPI} api - Interface whose methods can be used to change the behavior of the custom token exchange.
*/
// TODO: Replace this mock function with your actual token validation logic.
// The implementation depends on the issuer, algorithm (e.g., RS256 vs HS256),
// and format of your specific subject_token.
async function validateToken(subjectToken) {
// Example structure:
// 1. Verify token signature
// 2. Validate standard claims (exp, iss, aud)
// 3. Return the payload if valid, or an error if invalid
// NOTE: This is a placeholder. You must implement strong validation here.
return {
isValid: true, // or false based on your validation
payload: {
sub: "auth0|user123",
"https://your-domain.com/org_id": "org_xyz" // Your target org ID
},
error: null
};
}
exports.onExecuteCustomTokenExchange = async (event, api) => {
const subjectToken = event.transaction.subject_token;
if (!subjectToken) {
return api.access.deny("invalid_request", "Missing subject_token");
}
// 1. Verify and Decode the incoming subject_token
const { isValid, payload, error } = await validateToken(subjectToken);
if (!isValid) {
console.error("Token verification failed:", error);
return api.access.rejectInvalidSubjectToken("Invalid subject_token provided.");
}
// 2. Extract data from the validated payload
const originalSub = payload.sub;
// Ensure this namespace exactly matches what you set in your Post-Login Action
const targetOrgId = payload['https://your-domain.com/org_id'];
// 3. Set the user context for the transaction
api.authentication.setUserById(originalSub);
// 4. Set the standard organization context if the custom claim exists
if (targetOrgId) {
api.authentication.setOrganization(targetOrgId);
console.log(`Successfully exchanged token for org_id: ${targetOrgId}`);
} else {
console.log("No custom org_id claim found in the subject token.");
}
};