@sudoplatform/sudo-user 11.0.0 → 12.0.1

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 (115) hide show
  1. package/cjs/client/apiClient.d.ts +25 -0
  2. package/cjs/client/apiClient.js +149 -0
  3. package/cjs/client/apiClient.js.map +1 -0
  4. package/cjs/core/api-key-names.d.ts +8 -0
  5. package/cjs/core/api-key-names.js +12 -0
  6. package/cjs/core/api-key-names.js.map +1 -0
  7. package/cjs/core/auth-store.d.ts +51 -0
  8. package/cjs/core/auth-store.js +92 -0
  9. package/cjs/core/auth-store.js.map +1 -0
  10. package/cjs/core/crypto.d.ts +3 -0
  11. package/cjs/core/crypto.js +3 -0
  12. package/cjs/core/crypto.js.map +1 -0
  13. package/cjs/core/key-manager.d.ts +31 -0
  14. package/cjs/core/key-manager.js +113 -0
  15. package/cjs/core/key-manager.js.map +1 -0
  16. package/cjs/core/protected-store.d.ts +5 -0
  17. package/cjs/core/protected-store.js +3 -0
  18. package/cjs/core/protected-store.js.map +1 -0
  19. package/cjs/core/publisher.d.ts +6 -0
  20. package/cjs/core/publisher.js +3 -0
  21. package/cjs/core/publisher.js.map +1 -0
  22. package/cjs/core/sdk-config.d.ts +52 -0
  23. package/cjs/core/sdk-config.js +61 -0
  24. package/cjs/core/sdk-config.js.map +1 -0
  25. package/cjs/core/subscriber.d.ts +3 -0
  26. package/cjs/core/subscriber.js +3 -0
  27. package/cjs/core/subscriber.js.map +1 -0
  28. package/cjs/gen/graphql-types.d.ts +101 -0
  29. package/cjs/gen/graphql-types.js +105 -0
  30. package/cjs/gen/graphql-types.js.map +1 -0
  31. package/cjs/index.d.ts +4 -0
  32. package/cjs/index.js +13 -0
  33. package/cjs/index.js.map +1 -0
  34. package/cjs/runtimes/browser/browser-crypto.d.ts +2 -0
  35. package/cjs/runtimes/browser/browser-crypto.js +10 -0
  36. package/cjs/runtimes/browser/browser-crypto.js.map +1 -0
  37. package/cjs/runtimes/node/node-crypto.d.ts +2 -0
  38. package/cjs/runtimes/node/node-crypto.js +9 -0
  39. package/cjs/runtimes/node/node-crypto.js.map +1 -0
  40. package/cjs/sdk.d.ts +1 -0
  41. package/cjs/sdk.js +9 -0
  42. package/cjs/sdk.js.map +1 -0
  43. package/cjs/user/auth-provider.d.ts +97 -0
  44. package/cjs/user/auth-provider.js +116 -0
  45. package/cjs/user/auth-provider.js.map +1 -0
  46. package/cjs/user/auth.d.ts +64 -0
  47. package/cjs/user/auth.js +136 -0
  48. package/cjs/user/auth.js.map +1 -0
  49. package/cjs/user/error.d.ts +18 -0
  50. package/cjs/user/error.js +33 -0
  51. package/cjs/user/error.js.map +1 -0
  52. package/cjs/user/identity-provider.d.ts +73 -0
  53. package/cjs/user/identity-provider.js +245 -0
  54. package/cjs/user/identity-provider.js.map +1 -0
  55. package/cjs/user/user-client-interface.d.ts +168 -0
  56. package/cjs/user/user-client-interface.js +3 -0
  57. package/cjs/user/user-client-interface.js.map +1 -0
  58. package/cjs/user/user-client.d.ts +82 -0
  59. package/cjs/user/user-client.js +440 -0
  60. package/cjs/user/user-client.js.map +1 -0
  61. package/cjs/user/user-key-names.d.ts +6 -0
  62. package/cjs/user/user-key-names.js +10 -0
  63. package/cjs/user/user-key-names.js.map +1 -0
  64. package/cjs/utils/resolvable-promise.d.ts +9 -0
  65. package/cjs/utils/resolvable-promise.js +20 -0
  66. package/cjs/utils/resolvable-promise.js.map +1 -0
  67. package/docs/classes/AlreadyRegisteredError.html +4 -4
  68. package/docs/classes/IdentityServiceConfigNotFoundError.html +4 -4
  69. package/lib/client/apiClient.js +135 -180
  70. package/lib/client/apiClient.js.map +1 -0
  71. package/lib/core/api-key-names.js +8 -14
  72. package/lib/core/api-key-names.js.map +1 -0
  73. package/lib/core/auth-store.js +82 -138
  74. package/lib/core/auth-store.js.map +1 -0
  75. package/lib/core/crypto.js +2 -5
  76. package/lib/core/crypto.js.map +1 -0
  77. package/lib/core/key-manager.js +98 -148
  78. package/lib/core/key-manager.js.map +1 -0
  79. package/lib/core/protected-store.d.ts +5 -0
  80. package/lib/core/protected-store.js +2 -5
  81. package/lib/core/protected-store.js.map +1 -0
  82. package/lib/core/publisher.js +2 -5
  83. package/lib/core/publisher.js.map +1 -0
  84. package/lib/core/sdk-config.js +56 -56
  85. package/lib/core/sdk-config.js.map +1 -0
  86. package/lib/core/subscriber.js +2 -5
  87. package/lib/core/subscriber.js.map +1 -0
  88. package/lib/gen/graphql-types.js +96 -135
  89. package/lib/gen/graphql-types.js.map +1 -0
  90. package/lib/index.js +6 -67
  91. package/lib/index.js.map +1 -0
  92. package/lib/runtimes/browser/browser-crypto.d.ts +2 -0
  93. package/lib/runtimes/browser/browser-crypto.js +6 -14
  94. package/lib/runtimes/browser/browser-crypto.js.map +1 -0
  95. package/lib/runtimes/node/node-crypto.js +5 -11
  96. package/lib/runtimes/node/node-crypto.js.map +1 -0
  97. package/lib/sdk.js +6 -18
  98. package/lib/sdk.js.map +1 -0
  99. package/lib/user/auth-provider.js +81 -131
  100. package/lib/user/auth-provider.js.map +1 -0
  101. package/lib/user/auth.js +127 -179
  102. package/lib/user/auth.js.map +1 -0
  103. package/lib/user/error.js +15 -33
  104. package/lib/user/error.js.map +1 -0
  105. package/lib/user/identity-provider.js +232 -261
  106. package/lib/user/identity-provider.js.map +1 -0
  107. package/lib/user/user-client-interface.js +2 -5
  108. package/lib/user/user-client-interface.js.map +1 -0
  109. package/lib/user/user-client.js +423 -510
  110. package/lib/user/user-client.js.map +1 -0
  111. package/lib/user/user-key-names.js +6 -12
  112. package/lib/user/user-key-names.js.map +1 -0
  113. package/lib/utils/resolvable-promise.js +13 -19
  114. package/lib/utils/resolvable-promise.js.map +1 -0
  115. package/package.json +22 -21
@@ -0,0 +1,168 @@
1
+ import { SudoKeyManager } from '@sudoplatform/sudo-common';
2
+ import { AuthenticationProvider } from './auth-provider';
3
+ /**
4
+ * Encapsulates the authentication tokens obtained from a successful authentication.
5
+ *
6
+ * @param idToken ID token containing the user's identity attributes.
7
+ * @param accessToken access token required for authorizing API access.
8
+ * @param refreshToken refresh token used for refreshing ID and access tokens.
9
+ * @param tokenExpiry expiry of ID and access tokens in milliseconds.
10
+ */
11
+ export interface AuthenticationTokens {
12
+ idToken: string;
13
+ accessToken: string;
14
+ refreshToken: string;
15
+ tokenExpiry: number;
16
+ }
17
+ /**
18
+ * Interface encapsulating a library of functions for calling Sudo Platform identity service,
19
+ * handling federated sign in, managing keys and performing cryptographic operations.
20
+ */
21
+ export interface SudoUserClient {
22
+ /**
23
+ * Presents the sign in UI for federated sign in using an external identity provider.
24
+ */
25
+ presentFederatedSignInUI(): void;
26
+ /**
27
+ * Processes tokens from federated sign in returned to the specified URL.
28
+ * The tokens are passed to the web app via a redirect URL.
29
+ *
30
+ * @param url callback URL containing the tokens.
31
+ * @return Successful authentication result AuthenticationTokens.
32
+ */
33
+ processFederatedSignInTokens(url: string): Promise<AuthenticationTokens>;
34
+ /**
35
+ * Returns the ID token cached from the last sign-in.
36
+ *
37
+ * @return ID token.
38
+ */
39
+ getIdToken(): Promise<string | undefined>;
40
+ /**
41
+ * Returns the access token cached from the last sign-in.
42
+ *
43
+ * @return access token.
44
+ */
45
+ getAccessToken(): Promise<string | undefined>;
46
+ /**
47
+ * Returns the refresh token cached from the last sign-in.
48
+ *
49
+ * @return refresh token.
50
+ */
51
+ getRefreshToken(): Promise<string | undefined>;
52
+ /**
53
+ * Returns the ID and access token expiry cached from the last sign-in.
54
+ *
55
+ * @return token expiry.
56
+ */
57
+ getTokenExpiry(): Promise<Date | undefined>;
58
+ /**
59
+ * Returns the refresh token expiry cached from the last sign-in.
60
+ *
61
+ * @return refresh token expiry.
62
+ */
63
+ getRefreshTokenExpiry(): Promise<Date | undefined>;
64
+ /**
65
+ * Indicates whether or not this client is signed in with Sudo Platform backend. The client is
66
+ * considered signed in if it cached valid ID, access and refresh tokens.
67
+ *
68
+ * @return *true* if the client is signed in.
69
+ */
70
+ isSignedIn(): Promise<boolean>;
71
+ /**
72
+ * Refresh the access and ID tokens using the refresh token.
73
+ *
74
+ * @param refreshToken refresh token used to refresh the access and ID tokens.
75
+ * @return Successful authentication result AuthenticationTokens containing refreshed tokens
76
+ */
77
+ refreshTokens(refreshToken: string): Promise<AuthenticationTokens>;
78
+ /**
79
+ * Retrieves the latest ID token. This is to be used by the AWS AppSync client.
80
+ *
81
+ * @returns the latest ID token
82
+ */
83
+ getLatestAuthToken(): Promise<string>;
84
+ /**
85
+ * Returns the user name associated with this client. The username maybe needed to contact
86
+ * the support team when diagnosing an issue related to a specific user.
87
+ *
88
+ * @return user name.
89
+ */
90
+ getUserName(): Promise<string | undefined>;
91
+ /**
92
+ * Sets the user name associated with this client.
93
+ *
94
+ * @param name user name.
95
+ */
96
+ setUserName(name: string): Promise<void>;
97
+ /**
98
+ * Returns the subject of the user associated with this client.
99
+ * Note: This is an internal method used by other Sudo platform SDKs.
100
+ *
101
+ * @return user subject.
102
+ */
103
+ getSubject(): Promise<string | undefined>;
104
+ /**
105
+ * Returns the specified claim associated with the user's identity.
106
+ *
107
+ * @param name claim name.
108
+ */
109
+ getUserClaim(name: string): Promise<any | undefined>;
110
+ /**
111
+ * Signs out the user from this device.
112
+ */
113
+ signOut(): Promise<void>;
114
+ /**
115
+ * Signs out the user from all devices.
116
+ */
117
+ globalSignOut(): Promise<void>;
118
+ /**
119
+ * Registers this client against the backend with an external authentication provider. The caller must
120
+ * implement AuthenticationProvider protocol to return the appropriate authentication token required
121
+ * to authorize the registration request.
122
+ *
123
+ * @param authenticationProvider authentication provider that provides the authentication token.
124
+ * @param registrationId registration ID to uniquely identify this registration request.
125
+ * @return user ID of the newly created user
126
+ */
127
+ registerWithAuthenticationProvider(authenticationProvider: AuthenticationProvider, registrationId?: string): Promise<string>;
128
+ /**
129
+ * Sign into the backend using an external authentication provider. Caller must implement
130
+ * *AuthenticationProvider* interface to return the appropriate authentication token associated with
131
+ * the external identity registered with *registerWithAuthenticationProvider*.
132
+ *
133
+ * @param authenticationProvider authentication provider that provides the authentication token.
134
+ * @return authentication tokens associated with the successful sign in.
135
+ */
136
+ signInWithAuthenticationProvider(authenticationProvider: AuthenticationProvider): Promise<AuthenticationTokens>;
137
+ /**
138
+ * Indicates whether or not this client is registered with Sudo Platform backend.
139
+ *
140
+ * @return *true* if the client is registered.
141
+ */
142
+ isRegistered(): Promise<boolean>;
143
+ /**
144
+ * Sign into the backend using a private key. The client must have created a private/public key pair via
145
+ * the *registerWithAuthenticationProvider* method.
146
+ *
147
+ * @return authentication tokens associated with the successful sign in.
148
+ */
149
+ signInWithKey(): Promise<AuthenticationTokens>;
150
+ /**
151
+ * Clears cached authentication tokens.
152
+ */
153
+ clearAuthenticationTokens(): Promise<void>;
154
+ /**
155
+ * Presents the Cognito hosted UI signout endpoint.
156
+ * When the endpoint is invoked, the hosted web app's cookies
157
+ * will be invalidated, but the user is not logged out of Cognito.
158
+ */
159
+ presentSignOutUI(): void;
160
+ /**
161
+ * Resets internal state and clears any cached data.
162
+ */
163
+ reset(): void;
164
+ /**
165
+ * Getter to retrieve the SudoKeyManager
166
+ */
167
+ readonly sudoKeyManager: SudoKeyManager;
168
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=user-client-interface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-client-interface.js","sourceRoot":"","sources":["../../src/user/user-client-interface.ts"],"names":[],"mappings":""}
@@ -0,0 +1,82 @@
1
+ import { Logger, SudoKeyManager } from '@sudoplatform/sudo-common';
2
+ import { AuthenticationProvider } from '..';
3
+ import { ApiClient } from '../client/apiClient';
4
+ import { AuthenticationStore } from '../core/auth-store';
5
+ import { Config } from '../core/sdk-config';
6
+ import { AuthUI } from './auth';
7
+ import { IdentityProvider } from './identity-provider';
8
+ import { AuthenticationTokens, SudoUserClient } from './user-client-interface';
9
+ export interface SudoUserOptions {
10
+ authenticationStore?: AuthenticationStore;
11
+ sudoKeyManager?: SudoKeyManager;
12
+ apiClient?: ApiClient;
13
+ authUI?: AuthUI;
14
+ identityProvider?: IdentityProvider;
15
+ launchUriFn?: (url: string) => void;
16
+ config?: Config;
17
+ logger?: Logger;
18
+ }
19
+ export declare class DefaultSudoUserClient implements SudoUserClient {
20
+ private config;
21
+ private keyManager;
22
+ private sudoUserKeyManager;
23
+ private authenticationStore;
24
+ private identityProvider;
25
+ private apiClient;
26
+ private logger;
27
+ private authUI?;
28
+ private launchUriFn?;
29
+ constructor(options?: SudoUserOptions);
30
+ get sudoKeyManager(): SudoKeyManager;
31
+ presentFederatedSignInUI(): void;
32
+ processFederatedSignInTokens(url: string): Promise<AuthenticationTokens>;
33
+ getIdToken(): Promise<string | undefined>;
34
+ getAccessToken(): Promise<string | undefined>;
35
+ getRefreshToken(): Promise<string | undefined>;
36
+ getTokenExpiry(): Promise<Date | undefined>;
37
+ getRefreshTokenExpiry(): Promise<Date | undefined>;
38
+ getUserName(): Promise<string | undefined>;
39
+ setUserName(name: string): Promise<void>;
40
+ getUserClaim(name: string): Promise<any | undefined>;
41
+ getSubject(): Promise<string | undefined>;
42
+ isSignedIn(): Promise<boolean>;
43
+ refreshTokens(refreshToken: string): Promise<AuthenticationTokens>;
44
+ getLatestAuthToken(): Promise<string>;
45
+ signOut(): Promise<void>;
46
+ globalSignOut(): Promise<void>;
47
+ registerWithAuthenticationProvider(authenticationProvider: AuthenticationProvider, registrationId?: string): Promise<string>;
48
+ signInWithAuthenticationProvider(authenticationProvider: AuthenticationProvider): Promise<AuthenticationTokens>;
49
+ isRegistered(): Promise<boolean>;
50
+ signInWithKey(): Promise<AuthenticationTokens>;
51
+ deregister(): Promise<void>;
52
+ clearAuthenticationTokens(): Promise<void>;
53
+ presentSignOutUI(): void;
54
+ reset(): void;
55
+ private getTokenClaim;
56
+ /**
57
+ * Store the authentication tokens and user id of the signed in user. This function
58
+ * is only called when a user has successfully signed in using a private key.
59
+ */
60
+ private storeTokens;
61
+ private registerFederatedIdAndRefreshTokens;
62
+ private generateRegistrationData;
63
+ private getAuthTokens;
64
+ /**
65
+ * Provide a custom auth UI. This is mainly used for unit testing (optional).
66
+ */
67
+ setAuthUI(authUI: AuthUI): void;
68
+ /**
69
+ * Provide a custom identity provider. This is mainly used for unit testing (optional).
70
+ */
71
+ setIdentityProvider(identityProvider: IdentityProvider): void;
72
+ /**
73
+ * Provide a custom function to open the sign in url.
74
+ * This is mainly used for unit testing (optional).
75
+ */
76
+ setLaunchUriFn(launchUriFn: (url: string) => void): void;
77
+ /**
78
+ * Provide a custom authentication store.
79
+ * This is mainly used for unit testing (optional).
80
+ */
81
+ setAuthenticationStore(authenticationStore: AuthenticationStore): void;
82
+ }
@@ -0,0 +1,440 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DefaultSudoUserClient = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const sudo_common_1 = require("@sudoplatform/sudo-common");
6
+ const sudo_web_crypto_provider_1 = require("@sudoplatform/sudo-web-crypto-provider");
7
+ const JWT = tslib_1.__importStar(require("jsonwebtoken"));
8
+ const uuid_1 = require("uuid");
9
+ const apiClient_1 = require("../client/apiClient");
10
+ const api_key_names_1 = require("../core/api-key-names");
11
+ const auth_store_1 = require("../core/auth-store");
12
+ const key_manager_1 = require("../core/key-manager");
13
+ const sdk_config_1 = require("../core/sdk-config");
14
+ const auth_1 = require("./auth");
15
+ const error_1 = require("./error");
16
+ const identity_provider_1 = require("./identity-provider");
17
+ const user_key_names_1 = require("./user-key-names");
18
+ class DefaultSudoUserClient {
19
+ constructor(options) {
20
+ this.logger = options?.logger ?? new sudo_common_1.DefaultLogger('SudoUser', 'warn');
21
+ this.config = options?.config ?? (0, sdk_config_1.getIdentityServiceConfig)();
22
+ this.authenticationStore =
23
+ options?.authenticationStore ?? new auth_store_1.AuthenticationStore();
24
+ if (options?.sudoKeyManager) {
25
+ this.sudoUserKeyManager = options?.sudoKeyManager;
26
+ }
27
+ else {
28
+ const cryptoProvider = new sudo_web_crypto_provider_1.WebSudoCryptoProvider('SudoUserClient', 'com.sudoplatform.appservicename');
29
+ this.sudoUserKeyManager = new sudo_common_1.DefaultSudoKeyManager(cryptoProvider);
30
+ }
31
+ this.keyManager = new key_manager_1.KeyManager(this.sudoUserKeyManager);
32
+ this.launchUriFn = options?.launchUriFn;
33
+ this.identityProvider =
34
+ options?.identityProvider ??
35
+ new identity_provider_1.CognitoUserPoolIdentityProvider(this.keyManager, this.config, this.logger);
36
+ const federatedSignInConfig = this.config?.federatedSignIn;
37
+ if (federatedSignInConfig) {
38
+ this.authUI =
39
+ options?.authUI ??
40
+ new auth_1.CognitoAuthUI(this.authenticationStore, federatedSignInConfig, this.keyManager, this.logger, this.launchUriFn);
41
+ }
42
+ this.apiClient =
43
+ options?.apiClient ??
44
+ new apiClient_1.ApiClient(this.config.identityService.region, this.config.identityService.apiUrl, this, this.logger);
45
+ }
46
+ get sudoKeyManager() {
47
+ return this.sudoUserKeyManager;
48
+ }
49
+ presentFederatedSignInUI() {
50
+ try {
51
+ this.authUI?.presentFederatedSignInUI();
52
+ }
53
+ catch (err) {
54
+ const error = err;
55
+ this.logger.error('Failed to launch the federated sign in UI.', { error });
56
+ throw new sudo_common_1.AuthenticationError(error.message);
57
+ }
58
+ }
59
+ async processFederatedSignInTokens(url) {
60
+ try {
61
+ let authTokens = await this.authUI?.processFederatedSignInTokens(url);
62
+ if (authTokens) {
63
+ authTokens = await this.registerFederatedIdAndRefreshTokens(authTokens);
64
+ return authTokens;
65
+ }
66
+ else {
67
+ throw new sudo_common_1.AuthenticationError('Authentication tokens missing.');
68
+ }
69
+ }
70
+ catch (err) {
71
+ const error = err;
72
+ this.logger.error('Failed to process the federated sign in redirect.', {
73
+ error,
74
+ });
75
+ throw new sudo_common_1.AuthenticationError(error.message);
76
+ }
77
+ }
78
+ async getIdToken() {
79
+ return await this.keyManager.getString(api_key_names_1.apiKeyNames.idToken);
80
+ }
81
+ async getAccessToken() {
82
+ return await this.keyManager.getString(api_key_names_1.apiKeyNames.accessToken);
83
+ }
84
+ async getRefreshToken() {
85
+ return await this.keyManager.getString(api_key_names_1.apiKeyNames.refreshToken);
86
+ }
87
+ async getTokenExpiry() {
88
+ const expiry = await this.keyManager.getString(api_key_names_1.apiKeyNames.tokenExpiry);
89
+ return expiry ? new Date(Number(expiry) * 1000) : undefined;
90
+ }
91
+ async getRefreshTokenExpiry() {
92
+ let expiry = undefined;
93
+ const timeSinceEpoch = await this.keyManager.getString(api_key_names_1.apiKeyNames.refreshTokenExpiry);
94
+ if (timeSinceEpoch) {
95
+ expiry = new Date(Number(timeSinceEpoch));
96
+ }
97
+ return expiry;
98
+ }
99
+ async getUserName() {
100
+ try {
101
+ return await this.keyManager.getString(user_key_names_1.userKeyNames.userId);
102
+ }
103
+ catch {
104
+ return undefined;
105
+ }
106
+ }
107
+ async setUserName(name) {
108
+ await this.keyManager.removeItem(user_key_names_1.userKeyNames.userId);
109
+ await this.keyManager.addString(user_key_names_1.userKeyNames.userId, name);
110
+ }
111
+ async getUserClaim(name) {
112
+ const idToken = await this.getIdToken();
113
+ if (!idToken)
114
+ return undefined;
115
+ return this.getTokenClaim(idToken, name);
116
+ }
117
+ async getSubject() {
118
+ return await this.getUserClaim('sub');
119
+ }
120
+ async isSignedIn() {
121
+ try {
122
+ const authTokens = await this.getAuthTokens();
123
+ const expiry = await this.getRefreshTokenExpiry();
124
+ if (authTokens &&
125
+ authTokens.idToken &&
126
+ authTokens.accessToken &&
127
+ authTokens.refreshToken &&
128
+ expiry &&
129
+ // Considered signed in up to 1 hour before the expiry of refresh token.
130
+ expiry.getTime() > new Date().getTime() + 60 * 60 * 1000) {
131
+ return true;
132
+ }
133
+ else {
134
+ return false;
135
+ }
136
+ }
137
+ catch (err) {
138
+ // The key manager throws a KeyNotFoundError if an item is not found
139
+ // in which case we want to indicate that the user is not signed in.
140
+ return false;
141
+ }
142
+ }
143
+ async refreshTokens(refreshToken) {
144
+ try {
145
+ const uid = await this.getUserName();
146
+ if (uid) {
147
+ const authTokens = await this.identityProvider.refreshTokens(refreshToken);
148
+ await this.storeTokens(uid, authTokens);
149
+ return authTokens;
150
+ }
151
+ else {
152
+ throw new sudo_common_1.NotRegisteredError('Not registered.');
153
+ }
154
+ }
155
+ catch (err) {
156
+ const error = err;
157
+ throw new sudo_common_1.AuthenticationError(error.message);
158
+ }
159
+ }
160
+ async getLatestAuthToken() {
161
+ try {
162
+ const idToken = await this.getIdToken();
163
+ const refreshToken = await this.getRefreshToken();
164
+ const expiry = await this.getTokenExpiry();
165
+ if (idToken && refreshToken && expiry) {
166
+ if (expiry.getTime() > new Date().getTime() + 600 * 1000) {
167
+ return idToken;
168
+ }
169
+ else {
170
+ try {
171
+ const authTokens = await this.refreshTokens(refreshToken);
172
+ return authTokens.idToken;
173
+ }
174
+ catch (error) {
175
+ this.logger.info('getLatestAuthToken: Token refresh failed', {
176
+ error,
177
+ });
178
+ // return an empty id token
179
+ return '';
180
+ }
181
+ }
182
+ }
183
+ else {
184
+ // If tokens are missing then it's likely due to the client not being signed in.
185
+ // Return an empty id token.
186
+ return '';
187
+ }
188
+ }
189
+ catch (err) {
190
+ // The key manager throws a KeyNotFoundError if an item is not found
191
+ // which would indicate that the client is likely not signed in.
192
+ // Return an empty id token.
193
+ return '';
194
+ }
195
+ }
196
+ async signOut() {
197
+ let refreshToken;
198
+ try {
199
+ refreshToken = await this.getRefreshToken();
200
+ }
201
+ catch (err) {
202
+ if (err instanceof sudo_common_1.KeyNotFoundError) {
203
+ throw new sudo_common_1.NotSignedInError();
204
+ }
205
+ else {
206
+ throw err;
207
+ }
208
+ }
209
+ if (refreshToken) {
210
+ await this.identityProvider.signOut(refreshToken);
211
+ }
212
+ else {
213
+ throw new sudo_common_1.NotSignedInError();
214
+ }
215
+ }
216
+ async globalSignOut() {
217
+ if (!(await this.isSignedIn())) {
218
+ throw new sudo_common_1.NotSignedInError();
219
+ }
220
+ await this.apiClient.globalSignOut();
221
+ this.clearAuthenticationTokens();
222
+ }
223
+ async registerWithAuthenticationProvider(authenticationProvider, registrationId) {
224
+ this.logger.info('Registering using an external authentication provider.');
225
+ if (!(await this.isRegistered())) {
226
+ const authInfo = await authenticationProvider.getAuthenticationInfo();
227
+ if (!authInfo.isValid()) {
228
+ throw new sudo_common_1.NotAuthorizedError('Authentication provider returned invalid authentication info.');
229
+ }
230
+ const token = authInfo.encode();
231
+ const jwt = JWT.decode(token, { complete: true });
232
+ let uid;
233
+ if (jwt && jwt.payload['sub']) {
234
+ uid = jwt.payload['sub'];
235
+ }
236
+ else {
237
+ uid = (0, uuid_1.v4)();
238
+ }
239
+ const publicKey = await this.generateRegistrationData();
240
+ const data = {
241
+ challengeType: authInfo.type,
242
+ answer: token,
243
+ registrationId: registrationId ? registrationId : (0, uuid_1.v4)(),
244
+ publicKey: JSON.stringify(publicKeyToRSAPublicKey(publicKey)),
245
+ };
246
+ const validationData = Object.keys(data).map((key) => {
247
+ return {
248
+ Name: key,
249
+ Value: data[key],
250
+ };
251
+ });
252
+ const userId = await this.identityProvider.register(uid, validationData);
253
+ if (userId) {
254
+ await this.setUserName(userId);
255
+ return userId;
256
+ }
257
+ else {
258
+ throw new sudo_common_1.RegisterError('Registration was successful but not user ID was returned.');
259
+ }
260
+ }
261
+ else {
262
+ throw new error_1.AlreadyRegisteredError();
263
+ }
264
+ }
265
+ async signInWithAuthenticationProvider(authenticationProvider) {
266
+ this.logger.info('Signing in using external authentication provider.');
267
+ const authInfo = await authenticationProvider.getAuthenticationInfo();
268
+ if (!authInfo.isValid()) {
269
+ throw new sudo_common_1.NotAuthorizedError('Authentication provider returned invalid authentication info.');
270
+ }
271
+ const uid = authInfo.getUsername();
272
+ const token = authInfo.encode();
273
+ const type = authInfo.type;
274
+ let authTokens = await this.identityProvider.signInWithToken(uid, token, type);
275
+ await this.storeTokens(uid, authTokens);
276
+ authTokens = await this.registerFederatedIdAndRefreshTokens(authTokens);
277
+ return authTokens;
278
+ }
279
+ async isRegistered() {
280
+ try {
281
+ const uid = await this.getUserName();
282
+ return uid ? true : false;
283
+ }
284
+ catch (err) {
285
+ return false;
286
+ }
287
+ }
288
+ async signInWithKey() {
289
+ this.logger.info('Signing in using private key.');
290
+ const uid = await this.getUserName();
291
+ const userKeyId = await this.keyManager.getString('userKeyId');
292
+ if (uid && userKeyId) {
293
+ let authTokens = await this.identityProvider.signInWithKey(uid, userKeyId);
294
+ if (authTokens) {
295
+ await this.storeTokens(uid, authTokens);
296
+ authTokens = await this.registerFederatedIdAndRefreshTokens(authTokens);
297
+ return authTokens;
298
+ }
299
+ else {
300
+ throw new sudo_common_1.FatalError('Unexpected error. Unable to sign in.');
301
+ }
302
+ }
303
+ else {
304
+ throw new sudo_common_1.NotRegisteredError('Not registered.');
305
+ }
306
+ }
307
+ async deregister() {
308
+ await this.apiClient.deregister();
309
+ await this.keyManager.reset();
310
+ this.clearAuthenticationTokens();
311
+ }
312
+ async clearAuthenticationTokens() {
313
+ await this.keyManager.removeItem(api_key_names_1.apiKeyNames.idToken);
314
+ await this.keyManager.removeItem(api_key_names_1.apiKeyNames.accessToken);
315
+ await this.keyManager.removeItem(api_key_names_1.apiKeyNames.refreshToken);
316
+ await this.keyManager.removeItem(api_key_names_1.apiKeyNames.tokenExpiry);
317
+ this.authenticationStore.reset();
318
+ if (this.authUI) {
319
+ this.authUI.reset();
320
+ }
321
+ }
322
+ presentSignOutUI() {
323
+ this.authUI?.presentSignOutUI();
324
+ }
325
+ reset() {
326
+ this.keyManager.reset();
327
+ this.apiClient.reset();
328
+ this.clearAuthenticationTokens();
329
+ }
330
+ getTokenClaim(token, name) {
331
+ let claim = undefined;
332
+ const decoded = JWT.decode(token, { complete: true });
333
+ if (decoded) {
334
+ claim = decoded.payload[name];
335
+ }
336
+ return claim;
337
+ }
338
+ /**
339
+ * Store the authentication tokens and user id of the signed in user. This function
340
+ * is only called when a user has successfully signed in using a private key.
341
+ */
342
+ async storeTokens(uid, authTokens) {
343
+ await this.keyManager.removeItem(user_key_names_1.userKeyNames.userId);
344
+ await this.keyManager.removeItem(api_key_names_1.apiKeyNames.idToken);
345
+ await this.keyManager.removeItem(api_key_names_1.apiKeyNames.accessToken);
346
+ await this.keyManager.removeItem(api_key_names_1.apiKeyNames.refreshToken);
347
+ await this.keyManager.removeItem(api_key_names_1.apiKeyNames.tokenExpiry);
348
+ await this.keyManager.addString(user_key_names_1.userKeyNames.userId, uid);
349
+ await this.keyManager.addString(api_key_names_1.apiKeyNames.idToken, authTokens.idToken);
350
+ await this.keyManager.addString(api_key_names_1.apiKeyNames.accessToken, authTokens.accessToken);
351
+ await this.keyManager.addString(api_key_names_1.apiKeyNames.refreshToken, authTokens.refreshToken);
352
+ const decoded = JWT.decode(authTokens.idToken, { complete: true });
353
+ if (decoded) {
354
+ const tokenExpiry = decoded.payload['exp'];
355
+ await this.keyManager.addString(api_key_names_1.apiKeyNames.tokenExpiry, tokenExpiry);
356
+ }
357
+ }
358
+ async registerFederatedIdAndRefreshTokens(authTokens) {
359
+ const identityId = this.getTokenClaim(authTokens.idToken, 'custom:identityId');
360
+ if (identityId) {
361
+ return authTokens;
362
+ }
363
+ else {
364
+ await this.apiClient.registerFederatedId({
365
+ idToken: authTokens.idToken,
366
+ });
367
+ // Refresh the ID token so the identity ID is present as a claim.
368
+ const newAuthTokens = await this.refreshTokens(authTokens.refreshToken);
369
+ return newAuthTokens;
370
+ }
371
+ }
372
+ async generateRegistrationData() {
373
+ const keyId = await this.keyManager.generateKeyPair();
374
+ await this.keyManager.removeItem(user_key_names_1.userKeyNames.userKeyId);
375
+ await this.keyManager.addString(user_key_names_1.userKeyNames.userKeyId, keyId);
376
+ const publicKey = await this.keyManager.exportPublicKey(keyId);
377
+ return publicKey;
378
+ }
379
+ async getAuthTokens() {
380
+ const idToken = await this.getIdToken();
381
+ const accessToken = await this.getAccessToken();
382
+ const refreshToken = await this.getRefreshToken();
383
+ const tokenExpiry = await this.getTokenExpiry();
384
+ if (idToken && accessToken && refreshToken && tokenExpiry) {
385
+ return {
386
+ idToken: idToken,
387
+ accessToken: accessToken,
388
+ refreshToken: refreshToken,
389
+ tokenExpiry: tokenExpiry.getTime(),
390
+ };
391
+ }
392
+ else {
393
+ return undefined;
394
+ }
395
+ }
396
+ /**
397
+ * Provide a custom auth UI. This is mainly used for unit testing (optional).
398
+ */
399
+ setAuthUI(authUI) {
400
+ this.authUI = authUI;
401
+ }
402
+ /**
403
+ * Provide a custom identity provider. This is mainly used for unit testing (optional).
404
+ */
405
+ setIdentityProvider(identityProvider) {
406
+ this.identityProvider = identityProvider;
407
+ }
408
+ /**
409
+ * Provide a custom function to open the sign in url.
410
+ * This is mainly used for unit testing (optional).
411
+ */
412
+ setLaunchUriFn(launchUriFn) {
413
+ this.launchUriFn = launchUriFn;
414
+ }
415
+ /**
416
+ * Provide a custom authentication store.
417
+ * This is mainly used for unit testing (optional).
418
+ */
419
+ setAuthenticationStore(authenticationStore) {
420
+ this.authenticationStore = authenticationStore;
421
+ }
422
+ }
423
+ exports.DefaultSudoUserClient = DefaultSudoUserClient;
424
+ /**
425
+ * Convert PublicKey of any format to an RSAPublicKey format public key
426
+ *
427
+ * @param publicKey PublicKey to convert
428
+ * @returns RSAPublicKey formatted PublicKey
429
+ */
430
+ function publicKeyToRSAPublicKey(publicKey) {
431
+ if (publicKey.keyFormat === sudo_common_1.PublicKeyFormat.RSAPublicKey) {
432
+ return publicKey;
433
+ }
434
+ return {
435
+ ...publicKey,
436
+ publicKey: publicKey.publicKey.replace('MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A', ''),
437
+ keyFormat: sudo_common_1.PublicKeyFormat.RSAPublicKey,
438
+ };
439
+ }
440
+ //# sourceMappingURL=user-client.js.map