-
Notifications
You must be signed in to change notification settings - Fork 115
/
auth.ts
240 lines (220 loc) · 6.88 KB
/
auth.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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
import { v4 as uuid } from 'uuid';
import { createHash } from 'node:crypto';
import { Resource } from './resource.js';
import {
URLForAdminConsentConfig,
URLForAuthenticationConfig,
CodeExchangeRequest,
PKCEAuthURL,
TokenExchangeRequest,
CodeExchangeResponse,
ProviderDetectParams,
ProviderDetectResponse,
TokenInfoResponse,
} from '../models/auth.js';
import { Overrides } from '../config.js';
import { NylasResponse } from '../models/response.js';
import { CreateGrantRequest, Grant } from '../models/grants.js';
/**
* @property requestBody The values to create the Grant with.
*/
interface CreateGrantParams {
requestBody: CreateGrantRequest;
}
/**
* A collection of authentication related API endpoints
*
* These endpoints allow for various functionality related to authentication.
* Also contains the Grants API and collection of provider API endpoints.
*/
export class Auth extends Resource {
/**
* Build the URL for authenticating users to your application with OAuth 2.0
* @param config The configuration for building the URL
* @return The URL for hosted authentication
*/
public urlForOAuth2(config: URLForAuthenticationConfig): string {
return this.urlAuthBuilder(config).toString();
}
/**
* Exchange an authorization code for an access token
* @param request The request parameters for the code exchange
* @return Information about the Nylas application
*/
public exchangeCodeForToken(
request: CodeExchangeRequest
): Promise<CodeExchangeResponse> {
if (!request.clientSecret) {
request.clientSecret = this.apiClient.apiKey;
}
return this.apiClient.request<CodeExchangeResponse>({
method: 'POST',
path: `/v3/connect/token`,
body: {
...request,
grantType: 'authorization_code',
},
});
}
/**
* Refresh an access token
* @param request The refresh token request
* @return The response containing the new access token
*/
public refreshAccessToken(
request: TokenExchangeRequest
): Promise<CodeExchangeResponse> {
if (!request.clientSecret) {
request.clientSecret = this.apiClient.apiKey;
}
return this.apiClient.request<CodeExchangeResponse>({
method: 'POST',
path: `/v3/connect/token`,
body: {
...request,
grantType: 'refresh_token',
},
});
}
/**
* Build the URL for authenticating users to your application with OAuth 2.0 and PKCE
* IMPORTANT: YOU WILL NEED TO STORE THE 'secret' returned to use it inside the CodeExchange flow
* @param config The configuration for building the URL
* @return The URL for hosted authentication
*/
public urlForOAuth2PKCE(config: URLForAuthenticationConfig): PKCEAuthURL {
const url = this.urlAuthBuilder(config);
// Add code challenge to URL generation
url.searchParams.set('code_challenge_method', 's256');
const secret = uuid();
const secretHash = this.hashPKCESecret(secret);
url.searchParams.set('code_challenge', secretHash);
// Return the url with secret & hashed secret
return { secret, secretHash, url: url.toString() };
}
/**
* Build the URL for admin consent authentication for Microsoft
* @param config The configuration for building the URL
* @return The URL for admin consent authentication
*/
public urlForAdminConsent(config: URLForAdminConsentConfig): string {
const configWithProvider = { ...config, provider: 'microsoft' };
const url = this.urlAuthBuilder(configWithProvider);
url.searchParams.set('response_type', 'adminconsent');
url.searchParams.set('credential_id', config.credentialId);
return url.toString();
}
/**
* Create a grant via Custom Authentication
* @return The created grant
*/
public customAuthentication({
requestBody,
overrides,
}: CreateGrantParams & Overrides): Promise<NylasResponse<Grant>> {
return this.apiClient.request<NylasResponse<Grant>>({
method: 'POST',
path: `/v3/connect/custom`,
body: requestBody,
overrides,
});
}
/**
* Revoke a token (and the grant attached to the token)
* @param token The token to revoke
* @return True if the token was revoked successfully
*/
public async revoke(token: string): Promise<boolean> {
await this.apiClient.request<undefined>({
method: 'POST',
path: `/v3/connect/revoke`,
queryParams: {
token,
},
});
return true;
}
/**
* Detect provider from email address
* @param params The parameters to include in the request
* @return The detected provider, if found
*/
public async detectProvider(
params: ProviderDetectParams
): Promise<NylasResponse<ProviderDetectResponse>> {
return this.apiClient.request<NylasResponse<ProviderDetectResponse>>({
method: 'POST',
path: `/v3/providers/detect`,
queryParams: params,
});
}
/**
* Get info about an ID token
* @param idToken The ID token to query.
* @return The token information
*/
public idTokenInfo(
idToken: string
): Promise<NylasResponse<TokenInfoResponse>> {
return this.getTokenInfo({ id_token: idToken });
}
/**
* Get info about an access token
* @param accessToken The access token to query.
* @return The token information
*/
public accessTokenInfo(
accessToken: string
): Promise<NylasResponse<TokenInfoResponse>> {
return this.getTokenInfo({ access_token: accessToken });
}
private urlAuthBuilder(config: Record<string, any>): URL {
const url = new URL(`${this.apiClient.serverUrl}/v3/connect/auth`);
url.searchParams.set('client_id', config.clientId);
url.searchParams.set('redirect_uri', config.redirectUri);
url.searchParams.set(
'access_type',
config.accessType ? config.accessType : 'online'
);
url.searchParams.set('response_type', 'code');
if (config.provider) {
url.searchParams.set('provider', config.provider);
}
if (config.loginHint) {
url.searchParams.set('login_hint', config.loginHint);
}
if (config.includeGrantScopes !== undefined) {
url.searchParams.set(
'include_grant_scopes',
config.includeGrantScopes.toString()
);
}
if (config.scope) {
url.searchParams.set('scope', config.scope.join(' '));
}
if (config.prompt) {
url.searchParams.set('prompt', config.prompt);
}
if (config.state) {
url.searchParams.set('state', config.state);
}
return url;
}
private hashPKCESecret(secret: string): string {
const hash = createHash('sha256')
.update(secret)
.digest('hex');
return Buffer.from(hash)
.toString('base64')
.replace(/=+$/, '');
}
private getTokenInfo(
params: Record<string, any>
): Promise<NylasResponse<TokenInfoResponse>> {
return this.apiClient.request<NylasResponse<TokenInfoResponse>>({
method: 'GET',
path: `/v3/connect/tokeninfo`,
queryParams: params,
});
}
}