@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.
- package/dist/common/definitions/common.constants.d.ts +6 -1
- package/dist/common/definitions/common.constants.js +5 -0
- package/dist/common/definitions/common.constants.js.map +1 -1
- package/dist/module/iam.module.js.map +1 -1
- package/dist/services/authentication/iam.authentication.definitions.d.ts +79 -16
- package/dist/services/authentication/iam.authentication.definitions.js +6 -9
- package/dist/services/authentication/iam.authentication.definitions.js.map +1 -1
- package/dist/services/authentication/iam.authentication.service.d.ts +10 -3
- package/dist/services/authentication/iam.authentication.service.js +30 -2
- package/dist/services/authentication/iam.authentication.service.js.map +1 -1
- package/dist/services/authenticationOAuth2/iam.authenticationOAuth2.definitions.d.ts +38 -0
- package/dist/services/{authenticationLocal/iam.authenticationLocal.definitions.js → authenticationOAuth2/iam.authenticationOAuth2.definitions.js} +1 -1
- package/dist/services/authenticationOAuth2/iam.authenticationOAuth2.definitions.js.map +1 -0
- package/dist/services/authenticationOAuth2/iam.authenticationOAuth2.service.d.ts +24 -0
- package/dist/services/authenticationOAuth2/iam.authenticationOAuth2.service.js +299 -0
- package/dist/services/authenticationOAuth2/iam.authenticationOAuth2.service.js.map +1 -0
- package/dist/services/authenticationOAuth2/index.d.ts +2 -0
- package/dist/services/authenticationOAuth2/index.js +19 -0
- package/dist/services/authenticationOAuth2/index.js.map +1 -0
- package/dist/services/authenticationUserLocal/iam.authenticationUserLocal.definitions.d.ts +12 -0
- package/dist/services/authenticationUserLocal/iam.authenticationUserLocal.definitions.js +3 -0
- package/dist/services/authenticationUserLocal/iam.authenticationUserLocal.definitions.js.map +1 -0
- package/dist/services/authenticationUserLocal/iam.authenticationUserLocal.service.d.ts +14 -0
- package/dist/services/authenticationUserLocal/iam.authenticationUserLocal.service.js +141 -0
- package/dist/services/authenticationUserLocal/iam.authenticationUserLocal.service.js.map +1 -0
- package/dist/services/authenticationUserLocal/index.d.ts +2 -0
- package/dist/services/{authenticationLocal → authenticationUserLocal}/index.js +2 -2
- package/dist/services/authenticationUserLocal/index.js.map +1 -0
- package/dist/services/authorization/iam.authorization.definitions.d.ts +33 -23
- package/dist/services/authorization/iam.authorization.definitions.js +7 -0
- package/dist/services/authorization/iam.authorization.definitions.js.map +1 -1
- package/dist/services/authorization/iam.authorization.service.d.ts +28 -13
- package/dist/services/authorization/iam.authorization.service.js +231 -125
- package/dist/services/authorization/iam.authorization.service.js.map +1 -1
- package/dist/services/index.d.ts +4 -2
- package/dist/services/index.js +4 -2
- package/dist/services/index.js.map +1 -1
- package/dist/services/mfa/iam.mfa.definitions.d.ts +21 -0
- package/dist/services/mfa/iam.mfa.definitions.js +8 -0
- package/dist/services/mfa/iam.mfa.definitions.js.map +1 -0
- package/dist/services/mfa/iam.mfa.service.d.ts +9 -0
- package/dist/services/mfa/iam.mfa.service.js +31 -0
- package/dist/services/mfa/iam.mfa.service.js.map +1 -0
- package/dist/services/mfa/index.d.ts +2 -0
- package/dist/services/{users → mfa}/index.js +2 -2
- package/dist/services/mfa/index.js.map +1 -0
- package/dist/services/tokenManager/iam.tokenManager.definitions.d.ts +14 -3
- package/dist/services/tokenManager/iam.tokenManager.definitions.js.map +1 -1
- package/dist/services/tokenManager/iam.tokenManager.service.d.ts +23 -9
- package/dist/services/tokenManager/iam.tokenManager.service.js +111 -43
- package/dist/services/tokenManager/iam.tokenManager.service.js.map +1 -1
- package/dist/services/userManager/iam.userManager.definitions.d.ts +45 -0
- package/dist/services/userManager/iam.userManager.definitions.js +8 -0
- package/dist/services/userManager/iam.userManager.definitions.js.map +1 -0
- package/dist/services/userManager/iam.userManager.service.d.ts +32 -0
- package/dist/services/userManager/iam.userManager.service.js +331 -0
- package/dist/services/userManager/iam.userManager.service.js.map +1 -0
- package/dist/services/userManager/index.d.ts +2 -0
- package/dist/services/userManager/index.js +19 -0
- package/dist/services/userManager/index.js.map +1 -0
- package/package.json +9 -8
- package/src/common/definitions/common.constants.ts +14 -0
- package/src/common/definitions/index.ts +1 -0
- package/src/index.ts +3 -0
- package/src/module/iam.definitions.ts +15 -0
- package/src/module/iam.module.ts +29 -0
- package/src/module/index.ts +2 -0
- package/src/services/authentication/iam.authentication.definitions.ts +100 -0
- package/src/services/authentication/iam.authentication.service.ts +103 -0
- package/src/services/authentication/index.ts +2 -0
- package/src/services/authenticationOAuth2/iam.authenticationOAuth2.definitions.ts +71 -0
- package/src/services/authenticationOAuth2/iam.authenticationOAuth2.service.ts +350 -0
- package/src/services/authenticationOAuth2/index.ts +2 -0
- package/src/services/authenticationUserLocal/iam.authenticationUserLocal.definitions.ts +29 -0
- package/src/services/authenticationUserLocal/iam.authenticationUserLocal.service.ts +171 -0
- package/src/services/authenticationUserLocal/index.ts +2 -0
- package/src/services/authorization/iam.authorization.definitions.ts +55 -0
- package/src/services/authorization/iam.authorization.service.ts +384 -0
- package/src/services/authorization/index.ts +2 -0
- package/src/services/index.ts +7 -0
- package/src/services/mfa/iam.mfa.definitions.ts +28 -0
- package/src/services/mfa/iam.mfa.service.ts +38 -0
- package/src/services/mfa/index.ts +2 -0
- package/src/services/tokenManager/iam.tokenManager.definitions.ts +61 -0
- package/src/services/tokenManager/iam.tokenManager.service.ts +290 -0
- package/src/services/tokenManager/index.ts +2 -0
- package/src/services/userManager/iam.userManager.definitions.ts +73 -0
- package/src/services/userManager/iam.userManager.service.ts +461 -0
- package/src/services/userManager/index.ts +2 -0
- package/dist/services/authenticationLocal/iam.authenticationLocal.definitions.d.ts +0 -11
- package/dist/services/authenticationLocal/iam.authenticationLocal.definitions.js.map +0 -1
- package/dist/services/authenticationLocal/iam.authenticationLocal.service.d.ts +0 -10
- package/dist/services/authenticationLocal/iam.authenticationLocal.service.js +0 -70
- package/dist/services/authenticationLocal/iam.authenticationLocal.service.js.map +0 -1
- package/dist/services/authenticationLocal/index.d.ts +0 -2
- package/dist/services/authenticationLocal/index.js.map +0 -1
- package/dist/services/users/iam.users.definitions.d.ts +0 -30
- package/dist/services/users/iam.users.definitions.js +0 -8
- package/dist/services/users/iam.users.definitions.js.map +0 -1
- package/dist/services/users/iam.users.service.d.ts +0 -16
- package/dist/services/users/iam.users.service.js +0 -93
- package/dist/services/users/iam.users.service.js.map +0 -1
- package/dist/services/users/index.d.ts +0 -2
- 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,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,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;
|