@node-c/domain-iam 1.0.0-alpha8 → 1.0.0-beta0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/dist/common/definitions/common.constants.d.ts +6 -1
  2. package/dist/common/definitions/common.constants.js +5 -0
  3. package/dist/common/definitions/common.constants.js.map +1 -1
  4. package/dist/module/iam.module.js.map +1 -1
  5. package/dist/services/authentication/iam.authentication.definitions.d.ts +79 -16
  6. package/dist/services/authentication/iam.authentication.definitions.js +6 -9
  7. package/dist/services/authentication/iam.authentication.definitions.js.map +1 -1
  8. package/dist/services/authentication/iam.authentication.service.d.ts +10 -3
  9. package/dist/services/authentication/iam.authentication.service.js +30 -2
  10. package/dist/services/authentication/iam.authentication.service.js.map +1 -1
  11. package/dist/services/authenticationOAuth2/iam.authenticationOAuth2.definitions.d.ts +38 -0
  12. package/dist/services/{authenticationLocal/iam.authenticationLocal.definitions.js → authenticationOAuth2/iam.authenticationOAuth2.definitions.js} +1 -1
  13. package/dist/services/authenticationOAuth2/iam.authenticationOAuth2.definitions.js.map +1 -0
  14. package/dist/services/authenticationOAuth2/iam.authenticationOAuth2.service.d.ts +24 -0
  15. package/dist/services/authenticationOAuth2/iam.authenticationOAuth2.service.js +299 -0
  16. package/dist/services/authenticationOAuth2/iam.authenticationOAuth2.service.js.map +1 -0
  17. package/dist/services/authenticationOAuth2/index.d.ts +2 -0
  18. package/dist/services/authenticationOAuth2/index.js +19 -0
  19. package/dist/services/authenticationOAuth2/index.js.map +1 -0
  20. package/dist/services/authenticationUserLocal/iam.authenticationUserLocal.definitions.d.ts +12 -0
  21. package/dist/services/authenticationUserLocal/iam.authenticationUserLocal.definitions.js +3 -0
  22. package/dist/services/authenticationUserLocal/iam.authenticationUserLocal.definitions.js.map +1 -0
  23. package/dist/services/authenticationUserLocal/iam.authenticationUserLocal.service.d.ts +14 -0
  24. package/dist/services/authenticationUserLocal/iam.authenticationUserLocal.service.js +141 -0
  25. package/dist/services/authenticationUserLocal/iam.authenticationUserLocal.service.js.map +1 -0
  26. package/dist/services/authenticationUserLocal/index.d.ts +2 -0
  27. package/dist/services/{authenticationLocal → authenticationUserLocal}/index.js +2 -2
  28. package/dist/services/authenticationUserLocal/index.js.map +1 -0
  29. package/dist/services/authorization/iam.authorization.definitions.d.ts +33 -23
  30. package/dist/services/authorization/iam.authorization.definitions.js +7 -0
  31. package/dist/services/authorization/iam.authorization.definitions.js.map +1 -1
  32. package/dist/services/authorization/iam.authorization.service.d.ts +28 -13
  33. package/dist/services/authorization/iam.authorization.service.js +231 -125
  34. package/dist/services/authorization/iam.authorization.service.js.map +1 -1
  35. package/dist/services/index.d.ts +4 -2
  36. package/dist/services/index.js +4 -2
  37. package/dist/services/index.js.map +1 -1
  38. package/dist/services/mfa/iam.mfa.definitions.d.ts +21 -0
  39. package/dist/services/mfa/iam.mfa.definitions.js +8 -0
  40. package/dist/services/mfa/iam.mfa.definitions.js.map +1 -0
  41. package/dist/services/mfa/iam.mfa.service.d.ts +9 -0
  42. package/dist/services/mfa/iam.mfa.service.js +31 -0
  43. package/dist/services/mfa/iam.mfa.service.js.map +1 -0
  44. package/dist/services/mfa/index.d.ts +2 -0
  45. package/dist/services/{users → mfa}/index.js +2 -2
  46. package/dist/services/mfa/index.js.map +1 -0
  47. package/dist/services/tokenManager/iam.tokenManager.definitions.d.ts +14 -3
  48. package/dist/services/tokenManager/iam.tokenManager.definitions.js.map +1 -1
  49. package/dist/services/tokenManager/iam.tokenManager.service.d.ts +23 -9
  50. package/dist/services/tokenManager/iam.tokenManager.service.js +111 -43
  51. package/dist/services/tokenManager/iam.tokenManager.service.js.map +1 -1
  52. package/dist/services/userManager/iam.userManager.definitions.d.ts +45 -0
  53. package/dist/services/userManager/iam.userManager.definitions.js +8 -0
  54. package/dist/services/userManager/iam.userManager.definitions.js.map +1 -0
  55. package/dist/services/userManager/iam.userManager.service.d.ts +32 -0
  56. package/dist/services/userManager/iam.userManager.service.js +331 -0
  57. package/dist/services/userManager/iam.userManager.service.js.map +1 -0
  58. package/dist/services/userManager/index.d.ts +2 -0
  59. package/dist/services/userManager/index.js +19 -0
  60. package/dist/services/userManager/index.js.map +1 -0
  61. package/package.json +9 -8
  62. package/src/common/definitions/common.constants.ts +14 -0
  63. package/src/common/definitions/index.ts +1 -0
  64. package/src/index.ts +3 -0
  65. package/src/module/iam.definitions.ts +15 -0
  66. package/src/module/iam.module.ts +29 -0
  67. package/src/module/index.ts +2 -0
  68. package/src/services/authentication/iam.authentication.definitions.ts +100 -0
  69. package/src/services/authentication/iam.authentication.service.ts +103 -0
  70. package/src/services/authentication/index.ts +2 -0
  71. package/src/services/authenticationOAuth2/iam.authenticationOAuth2.definitions.ts +71 -0
  72. package/src/services/authenticationOAuth2/iam.authenticationOAuth2.service.ts +350 -0
  73. package/src/services/authenticationOAuth2/index.ts +2 -0
  74. package/src/services/authenticationUserLocal/iam.authenticationUserLocal.definitions.ts +29 -0
  75. package/src/services/authenticationUserLocal/iam.authenticationUserLocal.service.ts +171 -0
  76. package/src/services/authenticationUserLocal/index.ts +2 -0
  77. package/src/services/authorization/iam.authorization.definitions.ts +55 -0
  78. package/src/services/authorization/iam.authorization.service.ts +384 -0
  79. package/src/services/authorization/index.ts +2 -0
  80. package/src/services/index.ts +7 -0
  81. package/src/services/mfa/iam.mfa.definitions.ts +28 -0
  82. package/src/services/mfa/iam.mfa.service.ts +38 -0
  83. package/src/services/mfa/index.ts +2 -0
  84. package/src/services/tokenManager/iam.tokenManager.definitions.ts +61 -0
  85. package/src/services/tokenManager/iam.tokenManager.service.ts +290 -0
  86. package/src/services/tokenManager/index.ts +2 -0
  87. package/src/services/userManager/iam.userManager.definitions.ts +73 -0
  88. package/src/services/userManager/iam.userManager.service.ts +461 -0
  89. package/src/services/userManager/index.ts +2 -0
  90. package/dist/services/authenticationLocal/iam.authenticationLocal.definitions.d.ts +0 -11
  91. package/dist/services/authenticationLocal/iam.authenticationLocal.definitions.js.map +0 -1
  92. package/dist/services/authenticationLocal/iam.authenticationLocal.service.d.ts +0 -10
  93. package/dist/services/authenticationLocal/iam.authenticationLocal.service.js +0 -70
  94. package/dist/services/authenticationLocal/iam.authenticationLocal.service.js.map +0 -1
  95. package/dist/services/authenticationLocal/index.d.ts +0 -2
  96. package/dist/services/authenticationLocal/index.js.map +0 -1
  97. package/dist/services/users/iam.users.definitions.d.ts +0 -30
  98. package/dist/services/users/iam.users.definitions.js +0 -8
  99. package/dist/services/users/iam.users.definitions.js.map +0 -1
  100. package/dist/services/users/iam.users.service.d.ts +0 -16
  101. package/dist/services/users/iam.users.service.js +0 -93
  102. package/dist/services/users/iam.users.service.js.map +0 -1
  103. package/dist/services/users/index.d.ts +0 -2
  104. package/dist/services/users/index.js.map +0 -1
@@ -0,0 +1,103 @@
1
+ import { ApplicationError, ConfigProviderService } from '@node-c/core';
2
+
3
+ import {
4
+ IAMAuthenticationCompleteData,
5
+ IAMAuthenticationCompleteOptions,
6
+ IAMAuthenticationCompleteResult,
7
+ IAMAuthenticationGetPayloadsFromExternalTokensData,
8
+ IAMAuthenticationGetPayloadsFromExternalTokensResult,
9
+ IAMAuthenticationGetUserCreateAccessTokenConfigResult,
10
+ IAMAuthenticationGetUserDataFromExternalTokenPayloadsData,
11
+ IAMAuthenticationGetUserDataFromExternalTokenPayloadsResult,
12
+ IAMAuthenticationInitiateData,
13
+ IAMAuthenticationInitiateOptions,
14
+ IAMAuthenticationInitiateResult,
15
+ IAMAuthenticationRefreshExternalAccessTokenData,
16
+ IAMAuthenticationRefreshExternalAccessTokenResult,
17
+ IAMAuthenticationVerifyExternalAccessTokenData,
18
+ IAMAuthenticationVerifyExternalAccessTokenResult
19
+ } from './iam.authentication.definitions';
20
+
21
+ export class IAMAuthenticationService<CompleteContext extends object, InitiateContext extends object> {
22
+ protected isLocal: boolean;
23
+
24
+ constructor(
25
+ // eslint-disable-next-line no-unused-vars
26
+ protected configProvider: ConfigProviderService,
27
+ // eslint-disable-next-line no-unused-vars
28
+ protected moduleName: string
29
+ ) {}
30
+
31
+ /*
32
+ * Step 2 of the auth process. Mandatory.
33
+ */
34
+ async complete(
35
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
36
+ _data: IAMAuthenticationCompleteData,
37
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
38
+ _options: IAMAuthenticationCompleteOptions<CompleteContext>
39
+ ): Promise<IAMAuthenticationCompleteResult> {
40
+ throw new ApplicationError(`[${this.moduleName}][IAMAuthenticationService]: Method "complete" not implemented.`);
41
+ }
42
+
43
+ getUserCreateAccessTokenConfig(): IAMAuthenticationGetUserCreateAccessTokenConfigResult {
44
+ throw new ApplicationError(
45
+ `[${this.moduleName}][IAMAuthenticationService]: Method "getUserAccessTokenConfig" not implemented.`
46
+ );
47
+ }
48
+
49
+ /*
50
+ * Method for decoding JWTs and returning their payloads.
51
+ * If the tokens aren't JWTs, other ways for retreiving the payloads can be implemented, such as the OAuth introspection endpoint.
52
+ */
53
+ async getPayloadsFromExternalTokens(
54
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
55
+ _data: IAMAuthenticationGetPayloadsFromExternalTokensData
56
+ ): Promise<IAMAuthenticationGetPayloadsFromExternalTokensResult> {
57
+ throw new ApplicationError(
58
+ `[${this.moduleName}][IAMAuthenticationService]: Method "getPayloadsFromExternalTokens" not implemented.`
59
+ );
60
+ }
61
+
62
+ /*
63
+ * Method for mapping token payload data, such as username and scopes, to local user data, such as email and roles.
64
+ */
65
+ async getUserDataFromExternalTokenPayloads(
66
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
67
+ _data: IAMAuthenticationGetUserDataFromExternalTokenPayloadsData
68
+ ): Promise<IAMAuthenticationGetUserDataFromExternalTokenPayloadsResult | null> {
69
+ throw new ApplicationError(
70
+ `[${this.moduleName}][IAMAuthenticationService]: Method "getUserDataFromExternalTokenPayloads" not implemented.`
71
+ );
72
+ }
73
+
74
+ /*
75
+ * Step 1 of the auth process. Mandatory.
76
+ */
77
+ async initiate(
78
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
79
+ _data: IAMAuthenticationInitiateData,
80
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
81
+ _options: IAMAuthenticationInitiateOptions<InitiateContext>
82
+ ): Promise<IAMAuthenticationInitiateResult> {
83
+ throw new ApplicationError(`[${this.moduleName}][IAMAuthenticationService]: Method "initiate" not implemented.`);
84
+ }
85
+
86
+ async refreshExternalAccessToken(
87
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
88
+ _data: IAMAuthenticationRefreshExternalAccessTokenData
89
+ ): Promise<IAMAuthenticationRefreshExternalAccessTokenResult> {
90
+ throw new ApplicationError(
91
+ `[${this.moduleName}][IAMAuthenticationService]: Method "refreshExternalAccessToken" not implemented.`
92
+ );
93
+ }
94
+
95
+ async verifyExternalAccessToken(
96
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
97
+ _data: IAMAuthenticationVerifyExternalAccessTokenData
98
+ ): Promise<IAMAuthenticationVerifyExternalAccessTokenResult> {
99
+ throw new ApplicationError(
100
+ `[${this.moduleName}][IAMAuthenticationService]: Method "verifyExternalAccessToken" not implemented.`
101
+ );
102
+ }
103
+ }
@@ -0,0 +1,2 @@
1
+ export * from './iam.authentication.definitions';
2
+ export * from './iam.authentication.service';
@@ -0,0 +1,71 @@
1
+ import {
2
+ IAMAuthenticationCompleteData,
3
+ IAMAuthenticationCompleteOptions,
4
+ IAMAuthenticationCompleteResult,
5
+ IAMAuthenticationGetPayloadsFromExternalTokensData,
6
+ IAMAuthenticationGetPayloadsFromExternalTokensResult,
7
+ IAMAuthenticationGetUserCreateAccessTokenConfigResult,
8
+ IAMAuthenticationInitiateData,
9
+ IAMAuthenticationInitiateOptions,
10
+ IAMAuthenticationInitiateResult,
11
+ IAMAuthenticationVerifyExternalAccessTokenData,
12
+ IAMAuthenticationVerifyExternalAccessTokenResult
13
+ } from '../authentication';
14
+
15
+ export interface IAMAuthenticationOAuth2AccessTokenProviderResponseData {
16
+ access_token: string;
17
+ expires_in?: number;
18
+ id_token?: string; // only available for OIDC
19
+ refresh_token?: string;
20
+ scope: string;
21
+ token_type: string;
22
+ }
23
+
24
+ export interface IAMAuthenticationOAuth2CompleteData extends IAMAuthenticationCompleteData {
25
+ code: string;
26
+ codeVerifier: string;
27
+ state: string;
28
+ }
29
+
30
+ export type IAMAuthenticationOAuth2CompleteOptions<Context extends object> = IAMAuthenticationCompleteOptions<Context>;
31
+
32
+ export interface IAMAuthenticationOAuth2CompleteResult extends IAMAuthenticationCompleteResult {
33
+ accessToken: string;
34
+ scope: string;
35
+ }
36
+
37
+ export type IAMAuthenticationOAuth2GetPayloadsFromExternalTokensData =
38
+ IAMAuthenticationGetPayloadsFromExternalTokensData;
39
+
40
+ export type IAMAuthenticationOAuth2GetPayloadsFromExternalTokensResult =
41
+ IAMAuthenticationGetPayloadsFromExternalTokensResult;
42
+
43
+ export type IAMAuthenticationOAuth2GetUserCreateAccessTokenConfigResult =
44
+ IAMAuthenticationGetUserCreateAccessTokenConfigResult;
45
+
46
+ export interface IAMAuthenticationOAuth2InitiateData extends IAMAuthenticationInitiateData {
47
+ scope?: string;
48
+ }
49
+
50
+ export interface IAMAuthenticationOAuth2InitiateOptions<Context extends object>
51
+ extends IAMAuthenticationInitiateOptions<Context> {
52
+ generateNonce?: boolean;
53
+ withPCKE?: boolean;
54
+ }
55
+
56
+ export interface IAMAuthenticationOAuth2InitiateResult extends IAMAuthenticationInitiateResult {
57
+ authorizationCodeRequestURL: string;
58
+ codeChallenge?: string;
59
+ codeVerifier?: string;
60
+ nonce?: string;
61
+ state: string;
62
+ }
63
+
64
+ export type IAMAuthenticationOAuth2VerifyExternalAccessTokenData = Pick<
65
+ IAMAuthenticationVerifyExternalAccessTokenData,
66
+ 'accessToken'
67
+ >;
68
+ export type IAMAuthenticationOAuth2VerifyExternalAccessTokenResult = Pick<
69
+ IAMAuthenticationVerifyExternalAccessTokenResult,
70
+ 'accessTokenPayload' | 'error'
71
+ >;
@@ -0,0 +1,350 @@
1
+ import crypto from 'crypto';
2
+
3
+ import {
4
+ AppConfigDomainIAM,
5
+ AppConfigDomainIAMAuthenticationStep,
6
+ ApplicationError,
7
+ ConfigProviderService,
8
+ HttpMethod,
9
+ base64UrlEncode,
10
+ httpRequest
11
+ } from '@node-c/core';
12
+
13
+ import * as jwt from 'jsonwebtoken';
14
+ import ld from 'lodash';
15
+
16
+ import {
17
+ IAMAuthenticationOAuth2AccessTokenProviderResponseData,
18
+ IAMAuthenticationOAuth2CompleteData,
19
+ IAMAuthenticationOAuth2CompleteOptions,
20
+ IAMAuthenticationOAuth2CompleteResult,
21
+ IAMAuthenticationOAuth2GetPayloadsFromExternalTokensData,
22
+ IAMAuthenticationOAuth2GetPayloadsFromExternalTokensResult,
23
+ IAMAuthenticationOAuth2GetUserCreateAccessTokenConfigResult,
24
+ IAMAuthenticationOAuth2InitiateData,
25
+ IAMAuthenticationOAuth2InitiateOptions,
26
+ IAMAuthenticationOAuth2InitiateResult,
27
+ IAMAuthenticationOAuth2VerifyExternalAccessTokenData,
28
+ IAMAuthenticationOAuth2VerifyExternalAccessTokenResult
29
+ } from './iam.authenticationOAuth2.definitions';
30
+
31
+ import { Constants } from '../../common/definitions';
32
+ import { IAMAuthenticationService } from '../authentication';
33
+
34
+ /*
35
+ * This method is meant to support the OAuth2.0 flow w/ a PKCE challenge. The default, non-PKCE flow is intentionally not supported, in preparation for the upcoming OAuth2.0 spec.
36
+ * The default case assumes the user is found based on the decoded access token content after the complete method, but these settings can be overwritten in the config for the authService.
37
+ * 1. IAMAuthenticationOAuth2Service.initiate
38
+ * 2. (outside of this service) Save the challenge, verifier and state in the data, linking it to the provided user.
39
+ * 3. (outside of this service) Send an authorization code request on the prvodied URL to the OAuth2.0 provider.
40
+ * 4. (outside of this service) Receive a response with the state and an authorization code.
41
+ * 5. (outside of this service) Find the previously saved data for the user based on the state and send it to this service, along with the repsonse data.
42
+ * 6. IAMAuthenticationOAuth2Service.complete
43
+ * 7. (outside this service) Generate a local access & refresh JWT pair with the same expiry time as the provider tokens.
44
+ * 8. (outside this service) Save the provider's access token and (refersh or ID) tokens in the data along with the JWTs, linking them to the user.
45
+ * *
46
+ * TODO: provider param name mapping, in case a specific provider has custom parameter names
47
+ * TODO: validate access_token flow - endpont
48
+ * TODO: refresh access_token flow - local (JWT), endpont
49
+ */
50
+ export class IAMAuthenticationOAuth2Service<
51
+ CompleteContext extends object,
52
+ InitiateContext extends object
53
+ > extends IAMAuthenticationService<CompleteContext, InitiateContext> {
54
+ constructor(
55
+ protected configProvider: ConfigProviderService,
56
+ protected moduleName: string,
57
+ // eslint-disable-next-line no-unused-vars
58
+ protected serviceName: string
59
+ ) {
60
+ super(configProvider, moduleName);
61
+ this.isLocal = false;
62
+ }
63
+
64
+ /*
65
+ * 6. IAMAuthenticationOAuth2Service.complete:
66
+ * Incoming for the http redirect - state & code
67
+ * 6.1. Send an access token request to the provider using the following params: grant_type=authorization_code, client_id, client_secret, redirect_uri, code, code_verifier.
68
+ * 6.2. Receive the access and refresh tokens - expires_in, access_token, scope, refresh_token OR id_token (OIDC only).
69
+ * 6.3. Return the access and (refresh or ID) tokens.
70
+ * TODO: the custom param mapping will potentially be needed here.
71
+ */
72
+ async complete(
73
+ data: IAMAuthenticationOAuth2CompleteData,
74
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
75
+ _options: IAMAuthenticationOAuth2CompleteOptions<CompleteContext>
76
+ ): Promise<IAMAuthenticationOAuth2CompleteResult> {
77
+ const { configProvider, moduleName, serviceName } = this;
78
+ const moduleConfig = configProvider.config.domain[moduleName] as AppConfigDomainIAM;
79
+ const { accessTokenGrantUrl, clientId, clientSecret, redirectUri } =
80
+ moduleConfig.authServiceSettings![serviceName].oauth2!;
81
+ if (!accessTokenGrantUrl) {
82
+ console.error(`[${moduleName}][${serviceName}]: Access token grant URL not configured.`);
83
+ throw new ApplicationError('Authentication failed.');
84
+ }
85
+ if (!redirectUri) {
86
+ console.error(`[${moduleName}][${serviceName}]: Redirect URI not configured.`);
87
+ throw new ApplicationError('Authentication failed.');
88
+ }
89
+ const { code, codeVerifier } = data;
90
+ const { data: providerResponseData, hasError } =
91
+ await httpRequest<IAMAuthenticationOAuth2AccessTokenProviderResponseData>(accessTokenGrantUrl, {
92
+ body: {
93
+ client_id: clientId,
94
+ client_secret: clientSecret,
95
+ code,
96
+ code_verifier: codeVerifier,
97
+ grant_type: 'authorization_code',
98
+ redirect_uri: redirectUri
99
+ },
100
+ isFormData: true,
101
+ method: HttpMethod.POST
102
+ });
103
+ if (hasError || !providerResponseData) {
104
+ console.error(
105
+ `[${moduleName}][${serviceName}]: Auhorization grant attempt failed for code "${code}".`,
106
+ providerResponseData
107
+ );
108
+ throw new ApplicationError('Authentication failed.');
109
+ }
110
+ return {
111
+ accessToken: providerResponseData.access_token,
112
+ accessTokenExpiresIn: providerResponseData.expires_in,
113
+ idToken: providerResponseData.id_token,
114
+ mfaUsed: true,
115
+ mfaValid: true,
116
+ refreshToken: providerResponseData.refresh_token,
117
+ scope: providerResponseData.scope,
118
+ valid: true
119
+ };
120
+ }
121
+
122
+ protected async generateChallenge(codeVerifier: string): Promise<string> {
123
+ const buffer = await crypto.subtle.digest(
124
+ Constants.OAUTH2_PKCE_CHALLENGE_HASH_METHOD,
125
+ new TextEncoder().encode(codeVerifier)
126
+ );
127
+ return base64UrlEncode(buffer);
128
+ }
129
+
130
+ protected generateUrlEncodedString(length: number): string {
131
+ const octetSize = Math.ceil((length * 3) / 4);
132
+ const octets = crypto.getRandomValues(new Uint8Array(octetSize));
133
+ return base64UrlEncode(octets.buffer).slice(0, length);
134
+ }
135
+
136
+ // TODO: introspect endpoint for non-JWTs
137
+ async getPayloadsFromExternalTokens(
138
+ data: IAMAuthenticationOAuth2GetPayloadsFromExternalTokensData
139
+ ): Promise<IAMAuthenticationOAuth2GetPayloadsFromExternalTokensResult> {
140
+ const { moduleName, serviceName } = this;
141
+ const { accessToken, idToken } = data;
142
+ const returnData: IAMAuthenticationOAuth2GetPayloadsFromExternalTokensResult = {};
143
+ if (accessToken) {
144
+ const { accessTokenPayload, error } = await this.verifyExternalAccessToken({
145
+ accessToken
146
+ });
147
+ if (error) {
148
+ console.error(
149
+ `[${moduleName}][${serviceName}]: Method "getPayloadsFromExternalTokens" has produced an error:`,
150
+ error
151
+ );
152
+ throw new ApplicationError(`[${moduleName}][${serviceName}]: Error getting data from external tokens.`);
153
+ }
154
+ returnData.accessTokenPayload = accessTokenPayload;
155
+ }
156
+ if (idToken) {
157
+ const idTokenData = await this.verifyToken(idToken);
158
+ returnData.idTokenPayload = idTokenData.content;
159
+ }
160
+ return returnData;
161
+ }
162
+
163
+ // Default config - plain OAuth2 without OIDC
164
+ getUserCreateAccessTokenConfig(): IAMAuthenticationOAuth2GetUserCreateAccessTokenConfigResult {
165
+ const { configProvider, moduleName, serviceName } = this;
166
+ const moduleConfig = configProvider.config.domain[moduleName] as AppConfigDomainIAM;
167
+ const { steps } = moduleConfig.authServiceSettings![serviceName];
168
+ const defaultConfig: IAMAuthenticationOAuth2GetUserCreateAccessTokenConfigResult = {
169
+ [AppConfigDomainIAMAuthenticationStep.Complete]: {
170
+ cache: {
171
+ settings: {
172
+ cacheFieldName: 'state',
173
+ inputFieldName: 'data.state'
174
+ },
175
+ use: {
176
+ data: { overwrite: true, use: true }
177
+ }
178
+ },
179
+ createUser: true,
180
+ decodeReturnedTokens: true,
181
+ findUser: true,
182
+ findUserBeforeAuth: false,
183
+ findUserInAuthResultBy: {
184
+ userFieldName: 'email',
185
+ resultFieldName: 'accessTokenPayload.username'
186
+ },
187
+ useReturnedTokens: true,
188
+ validWithoutUser: false
189
+ },
190
+ [AppConfigDomainIAMAuthenticationStep.Initiate]: {
191
+ cache: {
192
+ populate: {
193
+ data: [{ cacheFieldName: 'codeVerifier', inputFieldName: 'result.codeVerifier' }]
194
+ },
195
+ settings: {
196
+ cacheFieldName: 'state',
197
+ inputFieldName: 'result.state'
198
+ }
199
+ },
200
+ findUser: false,
201
+ stepResultPublicFields: ['authorizationCodeRequestURL'],
202
+ validWithoutUser: true
203
+ }
204
+ };
205
+ return ld.merge(defaultConfig, steps || {});
206
+ }
207
+
208
+ /*
209
+ * OAuth2.0 flow w/ a PKCE challenge:
210
+ * 1. IAMAuthenticationOAuth2Service.initiate
211
+ * 1.1. Generate a PKCE code, code verifier for it and PKCE challenge based on them.
212
+ * 1.2. Generate a unique random "state" and a unique random "nonce" (for OIDC only, optional).
213
+ * 1.3. Generate an authorization code request URL. This URL contains the response_type=code, client_id, code_challenge, code_challenge_method, nonce, state, redirect_uri and scope. The code_challenge_method is usually S256.
214
+ * 1.4. Return the code, verifier, challenge, nonce, state and the URL.
215
+ * In this method, the only difference between the default OAuth2.0 flow and OIDC is that OIDC requires scope=oidc.
216
+ * TODO: the custom param mapping will potentially be needed here.
217
+ */
218
+ async initiate(
219
+ data: IAMAuthenticationOAuth2InitiateData,
220
+ options: IAMAuthenticationOAuth2InitiateOptions<InitiateContext>
221
+ ): Promise<IAMAuthenticationOAuth2InitiateResult> {
222
+ const { configProvider, moduleName, serviceName } = this;
223
+ const moduleConfig = configProvider.config.domain[moduleName] as AppConfigDomainIAM;
224
+ const { authorizationUrl, clientId, codeChallengeMethod, defaultScope, redirectUri } =
225
+ moduleConfig.authServiceSettings![serviceName].oauth2!;
226
+ const { scope } = data;
227
+ const { generateNonce, withPCKE } = options;
228
+ const finalScope = scope || defaultScope;
229
+ if (!authorizationUrl) {
230
+ console.error(`[${moduleName}][${serviceName}]: Authorization URL not configured.`);
231
+ throw new ApplicationError('Authentication failed.');
232
+ }
233
+ if (!redirectUri) {
234
+ console.error(`[${moduleName}][${serviceName}]: Redirect URI not configured.`);
235
+ throw new ApplicationError('Authentication failed.');
236
+ }
237
+ if (!finalScope) {
238
+ console.error(
239
+ `[${moduleName}][${serviceName}]: Either a scope in thwe input, or a configured default scope, is required..`
240
+ );
241
+ throw new ApplicationError('Authentication failed.');
242
+ }
243
+ const state = this.generateUrlEncodedString(16);
244
+ let challenge: string | undefined;
245
+ let nonce: string | undefined;
246
+ let verifier: string | undefined;
247
+ let url =
248
+ `${authorizationUrl}?` +
249
+ 'response_type=code&' +
250
+ `client_id=${clientId}&` +
251
+ `redirect_uri=${encodeURIComponent(redirectUri)}&` +
252
+ `scope=${encodeURIComponent(finalScope)}&` +
253
+ `state=${state}`;
254
+ if (withPCKE) {
255
+ verifier = this.generateUrlEncodedString(Constants.OAUTH2_CODE_VERIFIER_LENGTH);
256
+ challenge = await this.generateChallenge(verifier);
257
+ url += `&code_challenge=${challenge}&code_challenge_method=${codeChallengeMethod}`;
258
+ }
259
+ if (generateNonce) {
260
+ nonce = this.generateUrlEncodedString(16);
261
+ url += `&nonce=${nonce}`;
262
+ }
263
+ return {
264
+ authorizationCodeRequestURL: url,
265
+ codeChallenge: challenge,
266
+ codeVerifier: verifier,
267
+ mfaUsed: true,
268
+ mfaValid: true,
269
+ nonce,
270
+ state,
271
+ valid: true
272
+ };
273
+ }
274
+
275
+ // TODO: verification endpoint for non-JWTs
276
+ async verifyExternalAccessToken(
277
+ data: IAMAuthenticationOAuth2VerifyExternalAccessTokenData
278
+ ): Promise<IAMAuthenticationOAuth2VerifyExternalAccessTokenResult> {
279
+ const { configProvider, moduleName, serviceName } = this;
280
+ const moduleConfig = configProvider.config.domain[moduleName] as AppConfigDomainIAM;
281
+ const { accessTokenAudiences, issuerUri, verifyTokensLocally } =
282
+ moduleConfig.authServiceSettings![serviceName].oauth2!;
283
+ const { accessToken } = data;
284
+ if (!accessTokenAudiences) {
285
+ throw new ApplicationError(
286
+ `[${moduleName}][${serviceName}]: In method "verifyExternalAccessToken": accessTokenAudiences not configured.`
287
+ );
288
+ }
289
+ if (!issuerUri) {
290
+ throw new ApplicationError(
291
+ `[${moduleName}][${serviceName}]: In method "verifyExternalAccessToken": issuer URI not configured.`
292
+ );
293
+ }
294
+ if (verifyTokensLocally) {
295
+ const accessTokenData = await this.verifyToken(accessToken, {
296
+ audiences: accessTokenAudiences,
297
+ issuer: issuerUri
298
+ });
299
+ if (accessTokenData.error) {
300
+ // return { error: Constants.TOKEN_EXPIRED_ERROR };
301
+ return { error: accessTokenData.error };
302
+ }
303
+ return { accessTokenPayload: accessTokenData.content };
304
+ }
305
+ throw new ApplicationError(
306
+ `[${moduleName}][${serviceName}]: In method "verifyExternalAccessToken": verification via external endpoint not configured.`
307
+ );
308
+ }
309
+
310
+ protected async verifyToken<DecodedTokenContent = unknown>(
311
+ token: string,
312
+ options?: { audiences?: string[]; issuer?: string; secret?: string }
313
+ ): Promise<{ content?: DecodedTokenContent; error?: unknown }> {
314
+ const { audiences, issuer, secret } = options || {};
315
+ let returnData: { content?: DecodedTokenContent; error?: unknown } = {};
316
+ if (secret) {
317
+ returnData = await new Promise<{ content?: DecodedTokenContent; error?: unknown }>(resolve => {
318
+ jwt.verify(token, secret, (err, decoded) => {
319
+ if (err) {
320
+ resolve({ content: decoded as DecodedTokenContent, error: err });
321
+ }
322
+ resolve({ content: decoded as DecodedTokenContent });
323
+ });
324
+ });
325
+ } else {
326
+ const tokenContent = jwt.decode(token) as DecodedTokenContent & { aud?: string; exp?: number; iss?: string };
327
+ if (tokenContent.exp) {
328
+ // tokenContent.exp < new Date().valueOf()
329
+ let currentTimeStamp = `${new Date().valueOf()}`;
330
+ let expString = `${tokenContent.exp}`;
331
+ if (expString.length < currentTimeStamp.length) {
332
+ currentTimeStamp = currentTimeStamp.substring(0, expString.length);
333
+ } else if (expString.length > currentTimeStamp.length) {
334
+ expString = expString.substring(0, currentTimeStamp.length);
335
+ }
336
+ if (parseInt(expString, 10) < parseInt(currentTimeStamp, 10)) {
337
+ returnData.error = Constants.TOKEN_EXPIRED_ERROR;
338
+ }
339
+ }
340
+ if (tokenContent.aud && audiences && !audiences.includes(tokenContent.aud)) {
341
+ returnData.error = Constants.TOKEN_MISMATCHED_AUDIENCES_ERROR;
342
+ }
343
+ if (tokenContent.iss && issuer && issuer !== tokenContent.iss) {
344
+ returnData.error = Constants.TOKEN_MISMATCHED_ISSUER_ERROR;
345
+ }
346
+ returnData.content = tokenContent;
347
+ }
348
+ return returnData;
349
+ }
350
+ }
@@ -0,0 +1,2 @@
1
+ export * from './iam.authenticationOAuth2.definitions';
2
+ export * from './iam.authenticationOAuth2.service';
@@ -0,0 +1,29 @@
1
+ import {
2
+ IAMAuthenticationCompleteData,
3
+ IAMAuthenticationCompleteOptions,
4
+ IAMAuthenticationCompleteResult,
5
+ IAMAuthenticationGetUserCreateAccessTokenConfigResult,
6
+ IAMAuthenticationInitiateData,
7
+ IAMAuthenticationInitiateOptions,
8
+ IAMAuthenticationInitiateResult
9
+ } from '../authentication';
10
+
11
+ export type IAMAuthenticationUserLocalCompleteData = IAMAuthenticationCompleteData;
12
+
13
+ export type IAMAuthenticationUserLocalCompleteOptions<Context extends object> =
14
+ IAMAuthenticationCompleteOptions<Context>;
15
+
16
+ export type IAMAuthenticationUserLocalCompleteResult = IAMAuthenticationCompleteResult;
17
+
18
+ export type IAMAuthenticationUserLocalGetUserCreateAccessTokenConfigResult =
19
+ IAMAuthenticationGetUserCreateAccessTokenConfigResult;
20
+
21
+ export interface IAMAuthenticationUserLocalInitiateData extends IAMAuthenticationInitiateData {
22
+ password: string;
23
+ }
24
+
25
+ export type IAMAuthenticationUserLocalInitiateOptions<Context extends object> = IAMAuthenticationInitiateOptions<
26
+ { password: string } & Context
27
+ >;
28
+
29
+ export type IAMAuthenticationUserLocalInitiateResult = IAMAuthenticationInitiateResult;