diff --git a/README.md b/README.md index 2a02c58..31ae8f7 100644 --- a/README.md +++ b/README.md @@ -17,158 +17,20 @@ const { AuthressClient } = require('authress-sdk'); ## Getting Started -### Framework Examples -* [Express](./examples/expressMicroservice) - -### Generic Javascript Example -#### Authorize using a user token -```js -const { AuthressClient } = require('authress-sdk'); - -// What is my baseUrl? => API Host: https://authress.io/app/#/api?route=overview -const authressClient = new AuthressClient({ baseUrl: 'https://DOMAIN.api-REGION.authress.io' }); - -// on api route -[route('/resources/')] -async function getResource(resourceId) { - // Get the user token and pass it to authress - const authorizationToken = request.headers.get('authorization'); - authressClient.setToken(authorizationToken); - - // Check Authress to authorize the user - try { - await authressClient.userPermissions.authorizeUser(userId, `resources/${resourceId}`, 'READ'); - } catch (error) { - // Will throw except if the user is not authorized to read the resource - if (error.status === 404) { - return 404; - } - throw error; - } - - // On success, continue with the route code to load resource and return it - return { resource: {}, statusCode: 200 }; -``` - -#### Authorize with a service client -```js -const { AuthressClient } = require('authress-sdk'); - -// create an instance of the API class during service initialization -// Replace DOMAIN with the Authress domain for your account - -// Create a service client in the Authress management portal and past the access token here -// This will generate a token automatically instead of passing the user token to the api -const accessToken = 'eyJrZXlJ....'; -const authressClient = new AuthressClient({ baseUrl: 'https://DOMAIN.api-REGION.authress.io' }, accessToken); - -// on api route -[route('/resources/')] -async function getResource(resourceId) { - // Check Authress to authorize the user - try { - await authressClient.userPermissions.authorizeUser(userId, `resources/${resourceId}`, 'READ'); - } catch (error) { - // Will throw except if the user is not authorized to read the resource - if (error.status === 404) { - return 404; - } - throw error; - } - - // On success, continue with the route code to load resource and return it - return { resource: {}, statusCode: 200 }; -``` - -#### Creating resources -When a user creates a resource in your application, we want to ensure that they get access own that resource. - -You may receive **User does not have sufficient access to grant permissions to resources** as an error along with the status code **403**. This means that the service client or user jwt does not have access to create the access record. If using a service client, go to the Authress portal and create a one time record which grants the service client `Authress:Owner` to `Resources/` so that it can manage access records for these types of resources. +### Frequently Asked Questions +* Where do I get a user ID from? -```js -await authressClient.accessRecords.createRecord({ - name: `Access To New Resource ${NewResourceId}`, - users: [{ userId: requestUserId }], - statements: [{ - resources: [{ resourceUri: `Resources/${NewResourceId}` }], - // Owner by default gives full control over this new resource, including the ability to grant others access as well. - roles: ['Authress:Owner'] - }] -}); -``` - -#### Verifying a token using the token verifier -```js -const { TokenVerifier } = require('authress-sdk'); -const cookieManager = require('cookie'); - -try { - // Grab authorization cookie from the request, the best way to do this will be framework specific. - const cookies = cookieManager.parse(request.headers.cookie || ''); - const userToken = cookies.authorization || request.headers.Authorization.split(' ')[1]; - // What should my url be? => https://authress.io/app/#/setup?focus=domain - const userIdentity = await TokenVerifier('https://login.application.com', userToken); -} catch (error) { - console.log('User is unauthorized', error); - return { statusCode: 401 }; -} -``` +Every JWT contains a user ID, and you can pull it out from there using the `TokenVerifier` import or `verifyToken` method. For more details see [Authress JWT access tokens](https://authress.io/knowledge-base/docs/authentication/validating-jwts#authress-user-ids-and-a-jwt-access-token-example). -#### Make direct API requests -Authress supports extended functionality via the REST api, in specific cases it helps to make these direct calls. Each API call requires a URL and an access token. In the case you want use the access token for the user, directly pass it as the `bearer` in the `Authorization` header: -```js -const response = await client.get(url, { 'Authorization': `Bearer: ${userAccessToken}` }); -``` +### Method Documentation -In the case you want to make a request using the service client's secret key, use the `serviceClientTokenProvider` you've already configured: -```js -// Standard library configuration: -const { AuthressClient, ServiceClientTokenProvider } = require('authress-sdk'); -const accessToken = 'eyJrZXlJ....'; -const serviceClientTokenProvider = new ServiceClientTokenProvider(accessToken); -const authressClient = new AuthressClient({ baseUrl: 'https://DOMAIN.api-REGION.authress.io' }, serviceClientTokenProvider); +[SDK examples](./docs/methods.md) -// Get a temporary token and use it: -const temporaryServiceClientAccessToken = await serviceClientTokenProvider.getToken(); -const response = await client.get(url, { 'Authorization': `Bearer: ${temporaryServiceClientAccessToken}` }); -``` - -#### Paginating through a collection resource -Some of the resources in the API are paginated. These resources contain a `pagination.next.cursor` property when there is a next page. The cursor can be passed to query to fetch the next page. Here's an example usage: - -```js -const { AuthressClient } = require('authress-sdk'); -const authressClient = new AuthressClient({ baseUrl: 'https://DOMAIN.api-REGION.authress.io' }) - -// on api route -async function (resourceId) { - // Get the user token and pass it to authress - const authorizationToken = request.headers.get('authorization'); - authressClient.setToken(authorizationToken); - - // Get the users resources - const response = await authressClient.userPermissions.getUserResources(userId, `resources/*`, 10, null, 'READ'); - for (const resource of response.data.resources) { - // Iterate on resource - } - - // Get the next page: - const nextPageResponse = await authressClient.userPermissions.getUserResources(userId, `resources/*`, 10, response.data.pagination.next.cursor, 'READ'); - for (const resource of nextPageResponse.data.resources) { - // Iterate on resource - } +### Framework Examples +See all the available [Authress Starter Kits](https://github.com/search?q=org%3AAuthress+starter-kit&type=repositories) - // Get all the next pages: - let cursor = response.data.pagination?.next?.cursor; - while (cursor) { - const response = await authressClient.userPermissions.getUserResources(userId, `resources/*`, 10, cursor, 'READ'); - cursor = response.data.pagination?.next?.cursor; - for (const resource of response.data.resources) { - // Iterate on resource - } - } -} -``` +* [Express](https://github.com/Authress/express-starter-kit) +* [All other frameworks](https://github.com/search?q=org%3AAuthress+starter-kit&type=repositories) ## Contributions diff --git a/docs/methods.md b/docs/methods.md new file mode 100644 index 0000000..a8533bd --- /dev/null +++ b/docs/methods.md @@ -0,0 +1,152 @@ +## Commonly used functionality + +[All available SDK methods](../index.d.ts) + +### Authorize using a user token +```js +const { AuthressClient } = require('authress-sdk'); + +// What is my baseUrl? => API Host: https://authress.io/app/#/api?route=overview +const authressClient = new AuthressClient({ baseUrl: 'https://DOMAIN.api-REGION.authress.io' }); + +// on api route +[route('/resources/')] +async function getResource(resourceId) { + // Get the user token and pass it to authress + const authorizationToken = request.headers.get('authorization'); + authressClient.setToken(authorizationToken); + + // Check Authress to authorize the user + try { + await authressClient.userPermissions.authorizeUser(userId, `resources/${resourceId}`, 'READ'); + } catch (error) { + // Will throw except if the user is not authorized to read the resource + if (error.status === 404) { + return 404; + } + throw error; + } + + // On success, continue with the route code to load resource and return it + return { resource: {}, statusCode: 200 }; +``` + +### Authorize with a service client +```js +const { AuthressClient } = require('authress-sdk'); + +// create an instance of the API class during service initialization +// Replace DOMAIN with the Authress domain for your account + +// Create a service client in the Authress management portal and past the access token here +// This will generate a token automatically instead of passing the user token to the api +const accessToken = 'eyJrZXlJ....'; +const authressClient = new AuthressClient({ baseUrl: 'https://DOMAIN.api-REGION.authress.io' }, accessToken); + +// on api route +[route('/resources/')] +async function getResource(resourceId) { + // Check Authress to authorize the user + try { + await authressClient.userPermissions.authorizeUser(userId, `resources/${resourceId}`, 'READ'); + } catch (error) { + // Will throw except if the user is not authorized to read the resource + if (error.status === 404) { + return 404; + } + throw error; + } + + // On success, continue with the route code to load resource and return it + return { resource: {}, statusCode: 200 }; +``` + +### Creating resources +When a user creates a resource in your application, we want to ensure that they get access own that resource. + +You may receive **User does not have sufficient access to grant permissions to resources** as an error along with the status code **403**. This means that the service client or user jwt does not have access to create the access record. If using a service client, go to the Authress portal and create a one time record which grants the service client `Authress:Owner` to `Resources/` so that it can manage access records for these types of resources. + +```js +await authressClient.accessRecords.createRecord({ + name: `Access To New Resource ${NewResourceId}`, + users: [{ userId: requestUserId }], + statements: [{ + resources: [{ resourceUri: `Resources/${NewResourceId}` }], + // Owner by default gives full control over this new resource, including the ability to grant others access as well. + roles: ['Authress:Owner'] + }] +}); +``` + +### Verifying a token using the token verifier +```js +const { TokenVerifier } = require('authress-sdk'); +const cookieManager = require('cookie'); + +try { + // Grab authorization cookie from the request, the best way to do this will be framework specific. + const cookies = cookieManager.parse(request.headers.cookie || ''); + const userToken = cookies.authorization || request.headers.Authorization.split(' ')[1]; + // What should my url be? => https://authress.io/app/#/setup?focus=domain + const userIdentity = await TokenVerifier('https://login.application.com', userToken); +} catch (error) { + console.log('User is unauthorized', error); + return { statusCode: 401 }; +} +``` + +### Make direct API requests +Authress supports extended functionality via the REST api, in specific cases it helps to make these direct calls. Each API call requires a URL and an access token. In the case you want use the access token for the user, directly pass it as the `bearer` in the `Authorization` header: +```js +const response = await client.get(url, { 'Authorization': `Bearer: ${userAccessToken}` }); +``` + +In the case you want to make a request using the service client's secret key, use the `serviceClientTokenProvider` you've already configured: +```js +// Standard library configuration: +const { AuthressClient, ServiceClientTokenProvider } = require('authress-sdk'); +const accessToken = 'eyJrZXlJ....'; +const serviceClientTokenProvider = new ServiceClientTokenProvider(accessToken); +const authressClient = new AuthressClient({ baseUrl: 'https://DOMAIN.api-REGION.authress.io' }, serviceClientTokenProvider); + +// Get a temporary token and use it: +const temporaryServiceClientAccessToken = await serviceClientTokenProvider.getToken(); +const response = await client.get(url, { 'Authorization': `Bearer: ${temporaryServiceClientAccessToken}` }); +``` + +### Paginating through a collection resource +Some of the resources in the API are paginated. These resources contain a `pagination.next.cursor` property when there is a next page. The cursor can be passed to query to fetch the next page. Here's an example usage: + +```js +const { AuthressClient } = require('authress-sdk'); +const authressClient = new AuthressClient({ baseUrl: 'https://DOMAIN.api-REGION.authress.io' }) + +// on api route +async function (resourceId) { + // Get the user token and pass it to authress + const authorizationToken = request.headers.get('authorization'); + authressClient.setToken(authorizationToken); + + // Get the users resources + const response = await authressClient.userPermissions.getUserResources(userId, `resources/*`, 10, null, 'READ'); + for (const resource of response.data.resources) { + // Iterate on resource + } + + // Get the next page: + const nextPageResponse = await authressClient.userPermissions.getUserResources(userId, `resources/*`, 10, response.data.pagination.next.cursor, 'READ'); + for (const resource of nextPageResponse.data.resources) { + // Iterate on resource + } + + // Get all the next pages: + let cursor = response.data.pagination?.next?.cursor; + while (cursor) { + const response = await authressClient.userPermissions.getUserResources(userId, `resources/*`, 10, cursor, 'READ'); + cursor = response.data.pagination?.next?.cursor; + for (const resource of response.data.resources) { + // Iterate on resource + } + } +} +``` diff --git a/examples/expressMicroservice/README.md b/examples/expressMicroservice/README.md deleted file mode 100644 index e11d0b8..0000000 --- a/examples/expressMicroservice/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# Express Authress example -An example built specifically for using Authress with Express. - -## Middleware -The important part of the integration is to get the userId and Authress client to authorize the user. This is done by adding a middleware to parse out the caller, and one line in the service to validate this. - -## See the code -If you just want to see the code, it's available right here. Most of it is boilerplate to run the example the interesting part starts a bit lower down. - -* [Microservice.js](./expressMicroservice.js#L43) - -### Using the example -To test and run the example: -* `npm install` or `yarn` -* `npm start` or `yarn start` - -And then an example using curl, make sure to replace `TOKEN` with a validate token already registered in Authress. -* `curl -X GET http://localhost:8080/v1/resources/Resource1 -H"Authorization: Bearer TOKEN"` - -Or for a working examples -```sh -curl -X GET http://localhost:8080/v1/resources/Resource1 \ - -H"Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJwcm92aWRlcnx1c2VySWQiLCJuYW1lIjoiQXBwbGljYXRpb24gVXNlciIsImlhdCI6MTUxNjIzOTAyMiwiaXNzIjoiaHR0cHM6Ly9pZGVudGl0eS5wcm92aWRlci9vYXV0aDIiLCJhdWQiOiJFeHByZXNzTWljcm9zZXJ2aWNlIn0.aCiRCN2KPxg7t0d1gj5Fc6S1lUJOI1KV34RIEVIl22rhwxZRLLpRKOeu8O_9iLnxbV8CLQUawHR2N4N5mmDZOepGdmR2Exq7x5mg0nZMBTfjqzU4lT_KkULm8jnuEraK9uj82Aeh8h4vPV5_VAdPPrYMIaJIMcvfMCjjuZqUkmnKujgiHLLjgzfiyJ0kVe0e8DOrdaROQQGB5LC84EqOAhxOU0Ev3yfBJOkhCncMbNtbsem3wuZUMl-Y3-wCXgkxW5WQJWANoe6V55qaUkBaAygx5h2NGTqxOPeUEDjFw7zFupkJmX_TXbqSWyZ6fU8m2qIrEUCPvZOGfJgBW7WzOQ" - -``` diff --git a/examples/expressMicroservice/expressMicroservice.js b/examples/expressMicroservice/expressMicroservice.js deleted file mode 100644 index c0df68e..0000000 --- a/examples/expressMicroservice/expressMicroservice.js +++ /dev/null @@ -1,154 +0,0 @@ -const stringify = require('json-stringify-safe'); -const bodyParser = require('body-parser'); -const express = require('express'); -const http = require('http'); -const normalizeHeaderCase = require('header-case-normalizer'); - -/** - * The following sections are just express boiler plate and can be mostly ignored. - */ -const app = express(); -const api = express.Router(); -app.use('/', api); - -app.use((request, response, next) => { - // add normalized headers for ease of use - Object.keys(request.headers).map(key => { - request.headers[normalizeHeaderCase(key)] = request.headers[key]; - }); - - // Set CORS headers in response - response.set({ - 'Access-Control-Allow-Headers': 'Content-Type,X-Amz-Date,Authorization,X-Api-Key', - 'Access-Control-Allow-Methods': 'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT', - 'Access-Control-Allow-Origin': '*' - }); - next(); -}); - -// Body handling -api.use(bodyParser.text({ type: ['application/x-www-form-urlencoded', 'text/css', 'text/csv', 'text/html', 'text/plain', 'text/html'] })); -api.use(bodyParser.raw({ type: ['application/octet-stream', 'application/binary', 'image/*', 'audio/*', 'video/*', 'application/pdf', 'application/x-tar', 'application/zip'], limit: '10mb' })); -api.use(bodyParser.json({ type: '*/*', limit: '10mb' })); - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -api.use((error, req, res, next) => { - console.log(stringify({ title: 'Failed to parse the body', error })); - res.status(400).send({ title: 'Invalid body parsing, it is not a valid body object.' }); -}); - -/********************************************************************************************* */ -/********************************************************************************************* */ -/********************************************************************************************* */ -/** Start of Example section for integration with Authress */ - -/** Setup middleware */ -const base64url = require('base64url'); -const { AuthressClient, ServiceClientTokenProvider, UnauthorizedError } = require('authress-sdk'); - -// Some requests to authress request service client access and cannot use the user's JWT -// IMPORTANT: These should be replace with account specific values -const serviceClientAccessKey = 'eyJrZXlJZCI6IjRkZmM0MDJiLTZmOGUtNGZhNy1iYzM3LWZhMzFlMTVhNmRkYyIsInByaXZhdGVLZXkiOiItLS0tLUJFR0lOIFBSSVZBVE'; -const authressDomain = 'https://DOMAIN.api-REGION.authress.io'; -const authressServiceClient = new AuthressClient({ baseUrl: authressDomain }, new ServiceClientTokenProvider(serviceClientAccessKey)); - -api.use((request, response, next) => { - const authorizationHeader = request.headers.authorization; - if (!authorizationHeader) { - response.status(401).send({}); - return; - } - const token = authorizationHeader.replace(/Bearer\s+/i, '').trim(); - const jwt = JSON.parse(base64url.decode(token.split('.')[1])); - response.locals.userId = jwt.sub; - // Other requests to Authress can and should be done with the user's token when possible - response.locals.authressClient = new AuthressClient({ baseUrl: authressDomain }, () => token); - - next(); -}); - -/** Routes */ -// Get a resource -api.get('/v1/resources/:id', async (request, response, next) => { - try { - await response.locals.authressClient.userPermissions.authorizeUser(response.locals.userId, `resources/${request.params.id}`, 'READ'); - response.status(200).json({ - id: request.params.id - }); - } catch (error) { - next(error); - } -}); - -// Create a sub resource -api.post('/v1/resources/:id/sub-resource', async (request, response, next) => { - try { - await response.locals.authressClient.userPermissions.authorizeUser(response.locals.userId, `resources/${request.params.id}/sub-resource`, 'CREATE'); - // Create resource in DB - const newSubResourceId = 'new-sub-resource-1'; - await dbHandler.createResource('subResource', newSubResourceId); - - // Save the user's access to the new resource in Authress, NOTE: this uses the authressServiceClient instead of the user's specific authress client - await authressServiceClient.accessRecords.createRecord({ - // Leave blank for Authress to generate one on the fly, or specify a value if the app intends to update and make modifications at a later point. - recordId: 'RecordId', - name: `Access To New Resource ${newSubResourceId}`, - users: [{ userId: response.locals.userId }], - statements: [{ - resources: [{ resourceUri: `Resources/${newSubResourceId}` }], - // Owner by default gives full control over this new resource, including the ability to grant others access as well. - roles: ['Authress:Owner'] - }] - }); - - response.status(200).json({ - id: request.params.id - }); - } catch (error) { - next(error); - } -}); - -// Catch bad requests -api.use((error, req, res, next) => { - if (error instanceof UnauthorizedError) { - return res.status(403).json({ error: 'User does not have access to resource', userId: error.userId, resourceUri: error.resourceUri, permission: error.permission }); - } - return next(error); -}); - -/** End of Example section for integration with Authress */ -/********************************************************************************************* */ -/********************************************************************************************* */ -/********************************************************************************************* */ - -/** Here and below continues the boiler plat which is mostly unnecessary. It is added to make it easier to debug issues with the example. */ -// Default fall back handling -api.options(/.*/, (req, res) => { - res.status(200).json({}); -}); - -api.all(/.*/, (req, res) => { - res.status(404).json({ - statusCode: 404, - title: `Resource not found at ${req.originalUrl}` - }); -}); - -/** Global Error handler */ -// eslint-disable-next-line @typescript-eslint/no-unused-vars -api.use((error, req, res, next) => { - console.error(`Catch-all Error: ${error.stack || error} - ${stringify(error, null, 2)}`); - res.status(500).json({ - statusCode: 500, - title: 'Catch-all Error', - detail: error.stack || error.toString() - }); -}); - -const httpServer = http.createServer(app); -httpServer.setTimeout(60 * 1000); -const port = 8080; -httpServer.listen(port, () => { - console.log(`App Running on http://localhost:${port}`); -}); diff --git a/examples/expressMicroservice/package.json b/examples/expressMicroservice/package.json deleted file mode 100644 index cd64636..0000000 --- a/examples/expressMicroservice/package.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "expressmicroservice", - "version": "1.0.0", - "description": "", - "main": "expressMicroservice.js", - "scripts": { - "start": "nodemon expressMicroservice.js" - }, - "author": "", - "license": "ISC", - "dependencies": {}, - "devDependencies": { - "authress-sdk": "^1", - "base64url": "^3.0.1", - "body-parser": "^1.19.0", - "express": "^4.17.1", - "header-case-normalizer": "^1.0.3", - "json-stringify-safe": "^5.0.1", - "lodash": "^4.17.21", - "morgan": "^1.10.0", - "nodemon": "^2.0.4" - } -} diff --git a/index.d.ts b/index.d.ts index fa15df1..fc6e6fb 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1197,7 +1197,7 @@ export interface UserPermissionsApi { * @throws {UnauthorizedError} */ // @ts-ignore - authorizeUser(userId?: string, resourceUri: string, permission: string): Promise>; + authorizeUser(userId?: string | null, resourceUri: string, permission: string): Promise>; /** * Billable Permanently disable a token. To be used after the token has completed its use. Should be called on all tokens to ensure they are not active indefinitely. * @summary Disable a token @@ -1206,7 +1206,7 @@ export interface UserPermissionsApi { * @throws {ArgumentRequiredError} */ // @ts-ignore - disableUserToken(userId?: string, tokenId: string): Promise>; + disableUserToken(userId?: string | null, tokenId: string): Promise>; /** * Billable Get a summary of the permissions a user has to a particular resource. * @summary Get the permissions a user has to a resource. @@ -1215,7 +1215,7 @@ export interface UserPermissionsApi { * @throws {ArgumentRequiredError} */ // @ts-ignore - getUserPermissionsForResource(userId?: string, resourceUri: string): Promise>; + getUserPermissionsForResource(userId?: string | null, resourceUri: string): Promise>; /** * Billable Get a summary of the roles a user has to a particular resource. Users can be assigned roles from multiple access records, this may cause the same role to appear in the list more than once.
READ: Authress:UserPermissions/{userId} * @summary Get the roles a user has to a resource. @@ -1224,7 +1224,7 @@ export interface UserPermissionsApi { * @throws {ArgumentRequiredError} */ // @ts-ignore - getUserRolesForResource(userId?: string, resourceUri: string): Promise>; + getUserRolesForResource(userId?: string | null, resourceUri: string): Promise>; /** * Billable Get the users resources. Get the users resources. This result is a list of resource uris that a user has an explicit permission to, a user with * access to all sub resources will return an empty list and {accessToAllSubResources} will be populated. To get a user's list of resources in these cases, it is recommended to also check explicit access to the collection resource, using the authorizeUser endpoint. In the case that the user only has access to a subset of resources in a collection, the list will be paginated. * @summary Get the resources a user has to permission to. @@ -1235,7 +1235,7 @@ export interface UserPermissionsApi { * @param {string} [permission] A required ALLOW action to check for. Resources a user does not have this permission will not be returned. * @throws {ArgumentRequiredError} */ - getUserResources(userId?: string, resourceUri?: string, limit?: number, cursor?: Cursor, permission?: string): Promise>; + getUserResources(userId?: string | null, resourceUri?: string, limit?: number, cursor?: Cursor, permission?: string): Promise>; /** * Billable Get an Authress signed JWT access token using with userId as the sub. Additionally, can be configured to limit the permissions for this particular token and the length of time the token is valid. Token validation is real-time, so deleted tokens are restricted from being used as soon as they are deleted. This gives full control to the user and client creating the token. Client must have access to impersonating the user in order to generate tokens on their behalf. * @summary Request a user token with additional configuration @@ -1244,7 +1244,7 @@ export interface UserPermissionsApi { * @throws {ArgumentRequiredError} */ // @ts-ignore - requestUserToken(userId?: string, body: TokenRequest): Promise>; + requestUserToken(userId?: string | null, body: TokenRequest): Promise>; } /** @@ -1273,7 +1273,7 @@ export class AuthressClient { * @param {AuthressSettings} settings The authress settings * @param {Promise> | Function | string} [tokenProvider] An optional {@link ServiceClientTokenProvider} which is used to generate an Authress client with the service clients permissions. */ - constructor(settings: AuthressSettings, tokenProvider: (() => Promise) | (() => string) | ServiceClientTokenProvider | string); + constructor(settings: AuthressSettings, tokenProvider?: (() => Promise) | (() => string) | ServiceClientTokenProvider | string); /** * @summary The AccessRecords api