To issue accessToken for usage on protected resources you can:
- Ensure you have your api: api1.example.com, api2.example.com, api3.example.com
- Ensure you have defined extraClientMetadata
/** @See https://github.com/panva/node-oidc-provider/tree/main/docs#extraClientMetadata */
/** Allows for custom client metadata to be defined, validated, manipulated as well as for existing property validations to be extended. Existing properties are snakeCased on a Client instance (e.g. client.redirectUris), new properties (defined by this configuration) will be avaialable with their names verbatim (e.g. client['urn:example:client:my-property']) */
module.exports = {
/**
* isInternalClient: true | false, wether the client is for first party or for third party
*
* resourcesScopes: ressource scope clients is allowed to requested for
*
* allowedResources: ressource server client is allowed to request token for
*/
properties: ['allowedResources', 'resourcesScopes', "isInternalClient"],
validator: function extraClientMetadataValidator(ctx, key, value, metadata) {},
};
- You can register your apis on the idP
// src/oidc/config/resources-servers.js
// Define list of resources server
let resourcesList = {
};
// Register api 1 on the IdProvider
// Assume process.env.API1_BASE_URL = https://api1.example.com
resourcesList[process.env.API1_BASE_URL] = {
// These scope is accept the by the resource server to make authorization decisions
scope: 'offline_access api:query api:get api:post api:patch api:delete',
// Audience
audience: process.env.API1_BASE_URL,
// accessTokenFormat?: 'opaque' | 'jwt' | 'paseto'
// Please see https://github.com/panva/node-oidc-provider/tree/main/docs#getresourceserverinfo sections Resource Server (API) for more customization
accessTokenFormat: 'opaque',
};
// Register api 2 on the IdProvider
// Assume process.env.API2_BASE_URL = https://api2.example.com
resourcesList[process.env.API2_BASE_URL] = {
scope: 'offline_access api:query api:get api:post api:patch api:delete',
audience: process.env.API2_BASE_URL,
accessTokenFormat: 'opaque',
};
// Register api 3 on the IdProvider
// Assume process.env.API3_BASE_URL = https://api3.example.com
resourcesList[process.env.API3_BASE_URL] = {
scope: 'offline_access api:query api:get api:post api:patch api:delete',
audience: process.env.API3_BASE_URL,
accessTokenFormat: 'opaque',
};
- Now configure each client with it allowed resources server it can request accessToken for and related resource scope:
// src/oidc/config/clients.js
{
client_id: "client1",
// ...
// Other client meta data
// ...
// These below are clientExtraMetadata. Mandatory for api access_token usage
allowedResources: [process.env.API1_BASE_URL, process.env.API2_BASE_URL],
resourcesScopes: "offline_access api:query api:get api:post api:patch api:delete",
isInternalClient: true
},
{
client_id: "client2",
// ...
// Other clie
// ...
// These below are clientExtraMetadata. Mandatory for api access_token usage
allowedResources: [process.env.API1_BASE_URL, process.env.API3_BASE_URL],
resourcesScopes: "offline_access api:query api:get api:post api:patch api:delete",
isInternalClient: true
}
- Define your config.features.resourceIndicator properly like according to previous stuffs
/** @See https://github.com/panva/node-oidc-provider/tree/main/docs#featuresresourceindicators */
resourceIndicators: {
enabled: true,
defaultResource(ctx, client, oneOf) {
return Array.isArray(ctx.oidc.params?.resource)
? ctx.oidc.params?.resource[0]
: ctx.oidc.params?.resource;
},
useGrantedResource: async function useGrantedResource(ctx, model) {
return true;
},
getResourceServerInfo(ctx, resourceIndicator, client) {
// Ensure resourceIndicator is provided and this resource exist
if (!resourceIndicator || !resourcesList[resourceIndicator]) {
throw new errors.InvalidRequest("invalid_request", "Invalid resource server");
}
// Get this resource infos
var targetResourceServer = resourcesList[resourceIndicator];
// Client request access_token for api must defined these 2 metadata: allowResources, ressourcesScopes
if (!Array.isArray(client.allowedResources) || !(client.allowedResources.includes(resourceIndicator))) {
throw new errors.InvalidClientMetadata("invalid_client_metadata", "allowedResources & allowedResources are mandatory or you cannot request access token for this server");
}
// Now ensure client get access_token for scope it not defined
let clientAllowedScope = "";
if (client.resourcesScopes) {
var scopesList = client.resourcesScopes.split(' ');
clientAllowedScope = scopesList.filter((scopeItem) => {
return targetResourceServer.scope.includes(scopeItem);
});
} else {
throw new errors.InvalidClientMetadata("invalid_client_metadata", "Please specify at least one scope");
}
console.log(">>----Client ressource allowed:", client.allowedResources)
console.log(">>----Client ressource scopes:", client.resourcesScopes)
console.log(">>----Target ressource server is:", targetResourceServer)
console.log(">>----resourceIndicator is :", resourceIndicator)
console.log(">>----Client Scope allowed:", clientAllowedScope.join(' '))
// Update the acces_token ressource to issued
targetResourceServer.scope = clientAllowedScope.join(' ');
return targetResourceServer;
},
}
- Now you can issue accessToken targeting your apis even as jwt, passeto or opaque