-
Notifications
You must be signed in to change notification settings - Fork 56
/
token-accessor.ts
130 lines (114 loc) · 4.43 KB
/
token-accessor.ts
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import { ErrorWithCause } from '@sap-cloud-sdk/util';
import { JwtPayload } from './jsonwebtoken-type';
import { decodeJwt } from './jwt';
import { CachingOptions } from './cache';
import { clientCredentialsTokenCache } from './client-credentials-token-cache';
import {
getXsuaaServiceCredentials,
resolveService
} from './environment-accessor';
import { Service, XsuaaServiceCredentials } from './environment-accessor-types';
import { ResilienceOptions } from './resilience-options';
import { replaceSubdomain } from './subdomain-replacer';
import { getClientCredentialsToken, getUserToken } from './xsuaa-service';
/**
* Returns an access token that can be used to call the given service. The token is fetched via a client credentials grant with the credentials of the given service.
* If multiple instances of the provided service exist, the first instance will be selected.
* When a JWT is passed, the tenant of the JWT will be used when performing the grant.
* When no JWT is passed, the grant will be performed using the provider tenant.
*
* Throws an error if there is no instance of the given service type or the XSUAA service, or if the request to the XSUAA service fails.
* @param service - The type of the service or an instance of {@link Service}.
* @param options - Options to influence caching and resilience behavior (see {@link CachingOptions} and {@link ResilienceOptions}, respectively) and a JWT. By default, caching and usage of a circuit breaker are enabled.
* @returns Access token.
*/
export async function serviceToken(
service: string | Service,
options?: CachingOptions &
ResilienceOptions & {
jwt?: string | JwtPayload;
// TODO 2.0 Once the xssec supports caching remove all xsuaa related content here and use their cache.
xsuaaCredentials?: XsuaaServiceCredentials;
}
): Promise<string> {
const opts = {
useCache: true,
enableCircuitBreaker: true,
...options
};
service = resolveService(service);
const serviceCredentials = service.credentials;
// TODO 2.0 Once the xssec supports caching remove all xsuaa related content here and use their cache.
if (opts.useCache) {
const xsuaa = multiTenantXsuaaCredentials(options);
const cachedToken = clientCredentialsTokenCache.getToken(
xsuaa.url,
serviceCredentials.clientid
);
if (cachedToken) {
return cachedToken.access_token;
}
}
try {
const token = await getClientCredentialsToken(
service,
options?.jwt,
options
);
if (opts.useCache) {
const xsuaa = multiTenantXsuaaCredentials(options);
clientCredentialsTokenCache.cacheToken(
xsuaa.url,
serviceCredentials.clientid,
token
);
}
return token.access_token;
} catch (err) {
throw new ErrorWithCause(
`Could not fetch client credentials token for service of type "${service.label}".`,
err
);
}
}
/**
* Returns a jwt bearer token that can be used to call the given service.
* The token is fetched via a JWT bearer token grant using the user token + client credentials.
*
* Throws an error if there is no instance of the given service type or the XSUAA service, or if the request to the XSUAA service fails.
* @param jwt - The JWT of the user for whom the access token should be fetched.
* @param service - The type of the service or an instance of {@link Service}.
* @param options - Options to influence resilience behavior (see {@link ResilienceOptions}). By default, usage of a circuit breaker is enabled.
* @returns A jwt bearer token.
*/
export async function jwtBearerToken(
jwt: string,
service: string | Service,
options?: ResilienceOptions
): Promise<string> {
const resolvedService = resolveService(service);
const opts: ResilienceOptions = {
enableCircuitBreaker: true,
...options
};
return getUserToken(resolvedService, jwt, opts);
}
function multiTenantXsuaaCredentials(
options: {
jwt?: string | JwtPayload;
xsuaaCredentials?: XsuaaServiceCredentials;
} = {}
): XsuaaServiceCredentials {
const xsuaa = options.xsuaaCredentials
? { ...options.xsuaaCredentials }
: getXsuaaServiceCredentials(options.jwt);
if (options.jwt) {
const decodedJwt =
typeof options.jwt === 'string' ? decodeJwt(options.jwt) : options.jwt;
if (!decodedJwt.iss) {
throw Error('Property `iss` is missing in the provided user token.');
}
xsuaa.url = replaceSubdomain(decodedJwt.iss, xsuaa.url);
}
return xsuaa;
}