@cirrobio/react-auth 0.0.2 → 0.0.3
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/README.md +44 -44
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +46 -43
- package/src/amplify/amplify-auth-provider.tsx +70 -70
- package/src/amplify/auth-listener.ts +23 -23
- package/src/amplify/configure-amplify.ts +35 -35
- package/src/amplify/magic-link.ts +62 -62
- package/src/auth-context/authentication-context-provider.tsx +65 -65
- package/src/auth-context/authentication-context.tsx +15 -15
- package/src/auth-context/useAuthenticator.tsx +31 -31
- package/src/components/LoginModal.tsx +109 -109
- package/src/components/LoginOptions.tsx +67 -67
- package/src/components/LoginWrapper.tsx +25 -25
- package/src/index.ts +12 -12
- package/src/models/auth-status.ts +1 -1
- package/src/static/static-token-provider.ts +44 -44
|
@@ -1,70 +1,70 @@
|
|
|
1
|
-
import { LoginProvider } from "@cirrobio/api-client";
|
|
2
|
-
import { configureAmplify } from "./configure-amplify";
|
|
3
|
-
import { fetchAuthSession } from "@aws-amplify/auth";
|
|
4
|
-
import {
|
|
5
|
-
AppConfig,
|
|
6
|
-
InteractiveAuthenticationProvider,
|
|
7
|
-
IRedeemSignInLink,
|
|
8
|
-
ISignInLinkRequest,
|
|
9
|
-
AuthEventHandlerOptions
|
|
10
|
-
} from "@cirrobio/react-core";
|
|
11
|
-
import { signInWithRedirect } from "aws-amplify/auth";
|
|
12
|
-
import { redeemSignInLink, requestSignInLink } from "./magic-link";
|
|
13
|
-
import { Hub } from "aws-amplify/utils";
|
|
14
|
-
import { amplifyAuthEventHandler } from "./auth-listener";
|
|
15
|
-
import { CurrentUser } from "@cirrobio/sdk";
|
|
16
|
-
import { signOut as amplifySignOut } from 'aws-amplify/auth';
|
|
17
|
-
|
|
18
|
-
export class AmplifyAuthProvider implements InteractiveAuthenticationProvider {
|
|
19
|
-
private loginProviders: LoginProvider[];
|
|
20
|
-
|
|
21
|
-
constructor(
|
|
22
|
-
readonly clientId?: string
|
|
23
|
-
) {
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
public getLoginProviders() {
|
|
27
|
-
return this.loginProviders;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
public configure(config: AppConfig): void {
|
|
31
|
-
configureAmplify(config, this.clientId);
|
|
32
|
-
this.loginProviders = config.tenantInfo.loginProviders;
|
|
33
|
-
console.log('AmplifyAuthProvider loaded with config');
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
public async getAccessToken(): Promise<string> {
|
|
37
|
-
const session = await fetchAuthSession();
|
|
38
|
-
return session.tokens.accessToken.toString();
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
public async getCurrentUser(): Promise<CurrentUser> {
|
|
42
|
-
const session = await fetchAuthSession();
|
|
43
|
-
const idToken = session.tokens.idToken.payload;
|
|
44
|
-
return CurrentUser.fromCognitoUser(idToken);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
public async forceRefresh(): Promise<void> {
|
|
48
|
-
await fetchAuthSession({ forceRefresh: true });
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
public async loginSSO(loginProvider: string): Promise<void> {
|
|
52
|
-
await signInWithRedirect({ provider: { custom: loginProvider } })
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
public async loginEmail(request: ISignInLinkRequest): Promise<void> {
|
|
56
|
-
await requestSignInLink(request);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
public async finishLoginEmail(request: IRedeemSignInLink): Promise<void> {
|
|
60
|
-
await redeemSignInLink(request);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
public async signOut(): Promise<void> {
|
|
64
|
-
await amplifySignOut();
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
public registerAuthEventHandler(options: AuthEventHandlerOptions): () => void {
|
|
68
|
-
return Hub.listen('auth', (data) => amplifyAuthEventHandler(data, options), 'authentication-provider');
|
|
69
|
-
}
|
|
70
|
-
}
|
|
1
|
+
import { LoginProvider } from "@cirrobio/api-client";
|
|
2
|
+
import { configureAmplify } from "./configure-amplify";
|
|
3
|
+
import { fetchAuthSession } from "@aws-amplify/auth";
|
|
4
|
+
import {
|
|
5
|
+
AppConfig,
|
|
6
|
+
InteractiveAuthenticationProvider,
|
|
7
|
+
IRedeemSignInLink,
|
|
8
|
+
ISignInLinkRequest,
|
|
9
|
+
AuthEventHandlerOptions
|
|
10
|
+
} from "@cirrobio/react-core";
|
|
11
|
+
import { signInWithRedirect } from "aws-amplify/auth";
|
|
12
|
+
import { redeemSignInLink, requestSignInLink } from "./magic-link";
|
|
13
|
+
import { Hub } from "aws-amplify/utils";
|
|
14
|
+
import { amplifyAuthEventHandler } from "./auth-listener";
|
|
15
|
+
import { CurrentUser } from "@cirrobio/sdk";
|
|
16
|
+
import { signOut as amplifySignOut } from 'aws-amplify/auth';
|
|
17
|
+
|
|
18
|
+
export class AmplifyAuthProvider implements InteractiveAuthenticationProvider {
|
|
19
|
+
private loginProviders: LoginProvider[];
|
|
20
|
+
|
|
21
|
+
constructor(
|
|
22
|
+
readonly clientId?: string
|
|
23
|
+
) {
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public getLoginProviders() {
|
|
27
|
+
return this.loginProviders;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public configure(config: AppConfig): void {
|
|
31
|
+
configureAmplify(config, this.clientId);
|
|
32
|
+
this.loginProviders = config.tenantInfo.loginProviders;
|
|
33
|
+
console.log('AmplifyAuthProvider loaded with config');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public async getAccessToken(): Promise<string> {
|
|
37
|
+
const session = await fetchAuthSession();
|
|
38
|
+
return session.tokens.accessToken.toString();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public async getCurrentUser(): Promise<CurrentUser> {
|
|
42
|
+
const session = await fetchAuthSession();
|
|
43
|
+
const idToken = session.tokens.idToken.payload;
|
|
44
|
+
return CurrentUser.fromCognitoUser(idToken);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
public async forceRefresh(): Promise<void> {
|
|
48
|
+
await fetchAuthSession({ forceRefresh: true });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
public async loginSSO(loginProvider: string): Promise<void> {
|
|
52
|
+
await signInWithRedirect({ provider: { custom: loginProvider } })
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
public async loginEmail(request: ISignInLinkRequest): Promise<void> {
|
|
56
|
+
await requestSignInLink(request);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
public async finishLoginEmail(request: IRedeemSignInLink): Promise<void> {
|
|
60
|
+
await redeemSignInLink(request);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
public async signOut(): Promise<void> {
|
|
64
|
+
await amplifySignOut();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
public registerAuthEventHandler(options: AuthEventHandlerOptions): () => void {
|
|
68
|
+
return Hub.listen('auth', (data) => amplifyAuthEventHandler(data, options), 'authentication-provider');
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
import { AuthHubEventData } from "@aws-amplify/core/src/Hub/types/AuthTypes";
|
|
2
|
-
import { AuthEventHandlerOptions } from "@cirrobio/react-core";
|
|
3
|
-
|
|
4
|
-
export const amplifyAuthEventHandler = ({ payload }, options: AuthEventHandlerOptions): void => {
|
|
5
|
-
const { event }: AuthHubEventData = payload;
|
|
6
|
-
const { onSignIn, onSignOut } = options ?? {};
|
|
7
|
-
switch (event) {
|
|
8
|
-
case 'signedIn': {
|
|
9
|
-
console.log('user has signed in successfully');
|
|
10
|
-
onSignIn();
|
|
11
|
-
break;
|
|
12
|
-
}
|
|
13
|
-
case 'signedOut':
|
|
14
|
-
case 'tokenRefresh_failure': {
|
|
15
|
-
onSignOut();
|
|
16
|
-
break;
|
|
17
|
-
}
|
|
18
|
-
default: {
|
|
19
|
-
console.info(event);
|
|
20
|
-
break;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
};
|
|
1
|
+
import { AuthHubEventData } from "@aws-amplify/core/src/Hub/types/AuthTypes";
|
|
2
|
+
import { AuthEventHandlerOptions } from "@cirrobio/react-core";
|
|
3
|
+
|
|
4
|
+
export const amplifyAuthEventHandler = ({ payload }, options: AuthEventHandlerOptions): void => {
|
|
5
|
+
const { event }: AuthHubEventData = payload;
|
|
6
|
+
const { onSignIn, onSignOut } = options ?? {};
|
|
7
|
+
switch (event) {
|
|
8
|
+
case 'signedIn': {
|
|
9
|
+
console.log('user has signed in successfully');
|
|
10
|
+
onSignIn();
|
|
11
|
+
break;
|
|
12
|
+
}
|
|
13
|
+
case 'signedOut':
|
|
14
|
+
case 'tokenRefresh_failure': {
|
|
15
|
+
onSignOut();
|
|
16
|
+
break;
|
|
17
|
+
}
|
|
18
|
+
default: {
|
|
19
|
+
console.info(event);
|
|
20
|
+
break;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
};
|
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
import { Amplify } from "aws-amplify";
|
|
2
|
-
import { AppConfig } from "@cirrobio/react-core";
|
|
3
|
-
|
|
4
|
-
export function configureAmplify(config: AppConfig, clientIdOverride?: string): void {
|
|
5
|
-
Amplify.configure({
|
|
6
|
-
Auth: {
|
|
7
|
-
Cognito: {
|
|
8
|
-
userPoolId: config.auth.userPoolId,
|
|
9
|
-
userPoolClientId: clientIdOverride ?? config.auth.uiAppId,
|
|
10
|
-
loginWith: {
|
|
11
|
-
oauth: {
|
|
12
|
-
domain: config.auth.endpoint,
|
|
13
|
-
scopes: [
|
|
14
|
-
'phone',
|
|
15
|
-
'email',
|
|
16
|
-
'profile',
|
|
17
|
-
'openid',
|
|
18
|
-
'aws.cognito.signin.user.admin'
|
|
19
|
-
],
|
|
20
|
-
redirectSignIn: [window.location.origin],
|
|
21
|
-
redirectSignOut: [window.location.origin],
|
|
22
|
-
responseType: 'code',
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
},
|
|
26
|
-
},
|
|
27
|
-
API: {
|
|
28
|
-
GraphQL: {
|
|
29
|
-
endpoint: config.liveEndpoint,
|
|
30
|
-
region: config.region,
|
|
31
|
-
defaultAuthMode: 'userPool',
|
|
32
|
-
},
|
|
33
|
-
},
|
|
34
|
-
});
|
|
35
|
-
}
|
|
1
|
+
import { Amplify } from "aws-amplify";
|
|
2
|
+
import { AppConfig } from "@cirrobio/react-core";
|
|
3
|
+
|
|
4
|
+
export function configureAmplify(config: AppConfig, clientIdOverride?: string): void {
|
|
5
|
+
Amplify.configure({
|
|
6
|
+
Auth: {
|
|
7
|
+
Cognito: {
|
|
8
|
+
userPoolId: config.auth.userPoolId,
|
|
9
|
+
userPoolClientId: clientIdOverride ?? config.auth.uiAppId,
|
|
10
|
+
loginWith: {
|
|
11
|
+
oauth: {
|
|
12
|
+
domain: config.auth.endpoint,
|
|
13
|
+
scopes: [
|
|
14
|
+
'phone',
|
|
15
|
+
'email',
|
|
16
|
+
'profile',
|
|
17
|
+
'openid',
|
|
18
|
+
'aws.cognito.signin.user.admin'
|
|
19
|
+
],
|
|
20
|
+
redirectSignIn: [window.location.origin],
|
|
21
|
+
redirectSignOut: [window.location.origin],
|
|
22
|
+
responseType: 'code',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
API: {
|
|
28
|
+
GraphQL: {
|
|
29
|
+
endpoint: config.liveEndpoint,
|
|
30
|
+
region: config.region,
|
|
31
|
+
defaultAuthMode: 'userPool',
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
}
|
|
@@ -1,62 +1,62 @@
|
|
|
1
|
-
import { confirmSignIn, signIn } from 'aws-amplify/auth';
|
|
2
|
-
import { IRedeemSignInLink, ISignInLinkRequest } from "@cirrobio/react-core";
|
|
3
|
-
|
|
4
|
-
export const COGNITO_PROVIDER_ID = 'COGNITO';
|
|
5
|
-
export const LOGIN_SENT_SUCCESS_MSG = 'Check your email for the login link. If you don’t have an account, the link won’t be sent.';
|
|
6
|
-
|
|
7
|
-
export async function requestSignInLink({ username, redirectUri }: ISignInLinkRequest): Promise<void> {
|
|
8
|
-
const redirectUriToUse = redirectUri || window.location.origin + "/magic-link";
|
|
9
|
-
try {
|
|
10
|
-
const { nextStep } = await signIn({
|
|
11
|
-
username,
|
|
12
|
-
password: null,
|
|
13
|
-
options: {
|
|
14
|
-
authFlowType: "CUSTOM_WITHOUT_SRP",
|
|
15
|
-
},
|
|
16
|
-
});
|
|
17
|
-
if (nextStep.signInStep !== 'CONFIRM_SIGN_IN_WITH_CUSTOM_CHALLENGE') {
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const clientMetadata = {
|
|
22
|
-
signInMethod: "MAGIC_LINK",
|
|
23
|
-
redirectUri: redirectUriToUse,
|
|
24
|
-
hasMagicLink: "no",
|
|
25
|
-
}
|
|
26
|
-
await confirmSignIn({
|
|
27
|
-
challengeResponse: "cirro-is-awesome",
|
|
28
|
-
options: {
|
|
29
|
-
clientMetadata,
|
|
30
|
-
},
|
|
31
|
-
});
|
|
32
|
-
} catch (e) {
|
|
33
|
-
// Cleanup the error message
|
|
34
|
-
const errorMessage = e.toString();
|
|
35
|
-
if (errorMessage.includes('UserLambdaValidationException')) {
|
|
36
|
-
throw Error(errorMessage.split('failed with error')[1]);
|
|
37
|
-
} else if (errorMessage.includes('Incorrect username or password')) {
|
|
38
|
-
throw Error('Incorrect username or password.');
|
|
39
|
-
}
|
|
40
|
-
throw Error(errorMessage);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export async function redeemSignInLink({ username, challenge }: IRedeemSignInLink): Promise<void> {
|
|
45
|
-
await signIn({
|
|
46
|
-
username: username,
|
|
47
|
-
password: null,
|
|
48
|
-
options: {
|
|
49
|
-
authFlowType: "CUSTOM_WITHOUT_SRP",
|
|
50
|
-
},
|
|
51
|
-
});
|
|
52
|
-
const clientMetadata = {
|
|
53
|
-
signInMethod: "MAGIC_LINK",
|
|
54
|
-
hasMagicLink: "yes",
|
|
55
|
-
}
|
|
56
|
-
await confirmSignIn({
|
|
57
|
-
challengeResponse: challenge,
|
|
58
|
-
options: {
|
|
59
|
-
clientMetadata,
|
|
60
|
-
},
|
|
61
|
-
});
|
|
62
|
-
}
|
|
1
|
+
import { confirmSignIn, signIn } from 'aws-amplify/auth';
|
|
2
|
+
import { IRedeemSignInLink, ISignInLinkRequest } from "@cirrobio/react-core";
|
|
3
|
+
|
|
4
|
+
export const COGNITO_PROVIDER_ID = 'COGNITO';
|
|
5
|
+
export const LOGIN_SENT_SUCCESS_MSG = 'Check your email for the login link. If you don’t have an account, the link won’t be sent.';
|
|
6
|
+
|
|
7
|
+
export async function requestSignInLink({ username, redirectUri }: ISignInLinkRequest): Promise<void> {
|
|
8
|
+
const redirectUriToUse = redirectUri || window.location.origin + "/magic-link";
|
|
9
|
+
try {
|
|
10
|
+
const { nextStep } = await signIn({
|
|
11
|
+
username,
|
|
12
|
+
password: null,
|
|
13
|
+
options: {
|
|
14
|
+
authFlowType: "CUSTOM_WITHOUT_SRP",
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
if (nextStep.signInStep !== 'CONFIRM_SIGN_IN_WITH_CUSTOM_CHALLENGE') {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const clientMetadata = {
|
|
22
|
+
signInMethod: "MAGIC_LINK",
|
|
23
|
+
redirectUri: redirectUriToUse,
|
|
24
|
+
hasMagicLink: "no",
|
|
25
|
+
}
|
|
26
|
+
await confirmSignIn({
|
|
27
|
+
challengeResponse: "cirro-is-awesome",
|
|
28
|
+
options: {
|
|
29
|
+
clientMetadata,
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
} catch (e) {
|
|
33
|
+
// Cleanup the error message
|
|
34
|
+
const errorMessage = e.toString();
|
|
35
|
+
if (errorMessage.includes('UserLambdaValidationException')) {
|
|
36
|
+
throw Error(errorMessage.split('failed with error')[1]);
|
|
37
|
+
} else if (errorMessage.includes('Incorrect username or password')) {
|
|
38
|
+
throw Error('Incorrect username or password.');
|
|
39
|
+
}
|
|
40
|
+
throw Error(errorMessage);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export async function redeemSignInLink({ username, challenge }: IRedeemSignInLink): Promise<void> {
|
|
45
|
+
await signIn({
|
|
46
|
+
username: username,
|
|
47
|
+
password: null,
|
|
48
|
+
options: {
|
|
49
|
+
authFlowType: "CUSTOM_WITHOUT_SRP",
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
const clientMetadata = {
|
|
53
|
+
signInMethod: "MAGIC_LINK",
|
|
54
|
+
hasMagicLink: "yes",
|
|
55
|
+
}
|
|
56
|
+
await confirmSignIn({
|
|
57
|
+
challengeResponse: challenge,
|
|
58
|
+
options: {
|
|
59
|
+
clientMetadata,
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
}
|
|
@@ -1,65 +1,65 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import { ReactElement, useCallback, useEffect, useMemo, useState } from "react";
|
|
3
|
-
import { AuthenticatorContext, AuthenticatorContextType } from './authentication-context';
|
|
4
|
-
import { AuthStatus } from "../models/auth-status";
|
|
5
|
-
import { UserDetail } from "@cirrobio/api-client";
|
|
6
|
-
import { useAppConfig } from "@cirrobio/react-core";
|
|
7
|
-
import { CurrentUser } from "@cirrobio/sdk";
|
|
8
|
-
|
|
9
|
-
export type AuthenticationProviderProps = {
|
|
10
|
-
children: React.ReactNode;
|
|
11
|
-
fetchUserInfo?: boolean;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Manages the authentication state of the application.
|
|
16
|
-
* @param children - The child components to render within the provider.
|
|
17
|
-
* @param fetchUserInfo - If true, fetches detailed user information after authentication.
|
|
18
|
-
*/
|
|
19
|
-
export function AuthenticationContextProvider({ children, fetchUserInfo }: AuthenticationProviderProps): ReactElement {
|
|
20
|
-
const [authStatus, setAuthStatus] = useState<AuthStatus>('configuring');
|
|
21
|
-
const [authInfo, setAuthInfo] = useState<CurrentUser>(null);
|
|
22
|
-
const [userInfo, setUserInfo] = useState<UserDetail>(null);
|
|
23
|
-
const { dataService, authProvider } = useAppConfig();
|
|
24
|
-
|
|
25
|
-
const refresh = useCallback((): void => {
|
|
26
|
-
authProvider.getCurrentUser()
|
|
27
|
-
.then((currentUser) => {
|
|
28
|
-
setAuthStatus('authenticated');
|
|
29
|
-
setAuthInfo(currentUser);
|
|
30
|
-
if (fetchUserInfo) {
|
|
31
|
-
dataService.users.getUser({ username: currentUser.username })
|
|
32
|
-
.then((data) => setUserInfo(data))
|
|
33
|
-
.catch((error) => {
|
|
34
|
-
console.error('Failed to fetch user info:', error);
|
|
35
|
-
setUserInfo(null);
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
})
|
|
39
|
-
.catch(() => {
|
|
40
|
-
setAuthStatus('unauthenticated');
|
|
41
|
-
});
|
|
42
|
-
}, []);
|
|
43
|
-
|
|
44
|
-
const value: AuthenticatorContextType = useMemo(
|
|
45
|
-
() => ({ authStatus, authInfo, refresh, userInfo }),
|
|
46
|
-
[authStatus, authInfo, userInfo, refresh]
|
|
47
|
-
);
|
|
48
|
-
|
|
49
|
-
// Refresh auth state on page load
|
|
50
|
-
useEffect(() => {
|
|
51
|
-
refresh();
|
|
52
|
-
}, [refresh]);
|
|
53
|
-
|
|
54
|
-
// Refresh auth state in response to auth events
|
|
55
|
-
useEffect(() => {
|
|
56
|
-
const options = { onSignIn: refresh, onSignOut: refresh };
|
|
57
|
-
return authProvider.registerAuthEventHandler(options);
|
|
58
|
-
}, [refresh]);
|
|
59
|
-
|
|
60
|
-
return (
|
|
61
|
-
<AuthenticatorContext.Provider value={value}>
|
|
62
|
-
{children}
|
|
63
|
-
</AuthenticatorContext.Provider>
|
|
64
|
-
);
|
|
65
|
-
}
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { ReactElement, useCallback, useEffect, useMemo, useState } from "react";
|
|
3
|
+
import { AuthenticatorContext, AuthenticatorContextType } from './authentication-context';
|
|
4
|
+
import { AuthStatus } from "../models/auth-status";
|
|
5
|
+
import { UserDetail } from "@cirrobio/api-client";
|
|
6
|
+
import { useAppConfig } from "@cirrobio/react-core";
|
|
7
|
+
import { CurrentUser } from "@cirrobio/sdk";
|
|
8
|
+
|
|
9
|
+
export type AuthenticationProviderProps = {
|
|
10
|
+
children: React.ReactNode;
|
|
11
|
+
fetchUserInfo?: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Manages the authentication state of the application.
|
|
16
|
+
* @param children - The child components to render within the provider.
|
|
17
|
+
* @param fetchUserInfo - If true, fetches detailed user information after authentication.
|
|
18
|
+
*/
|
|
19
|
+
export function AuthenticationContextProvider({ children, fetchUserInfo }: AuthenticationProviderProps): ReactElement {
|
|
20
|
+
const [authStatus, setAuthStatus] = useState<AuthStatus>('configuring');
|
|
21
|
+
const [authInfo, setAuthInfo] = useState<CurrentUser>(null);
|
|
22
|
+
const [userInfo, setUserInfo] = useState<UserDetail>(null);
|
|
23
|
+
const { dataService, authProvider } = useAppConfig();
|
|
24
|
+
|
|
25
|
+
const refresh = useCallback((): void => {
|
|
26
|
+
authProvider.getCurrentUser()
|
|
27
|
+
.then((currentUser) => {
|
|
28
|
+
setAuthStatus('authenticated');
|
|
29
|
+
setAuthInfo(currentUser);
|
|
30
|
+
if (fetchUserInfo) {
|
|
31
|
+
dataService.users.getUser({ username: currentUser.username })
|
|
32
|
+
.then((data) => setUserInfo(data))
|
|
33
|
+
.catch((error) => {
|
|
34
|
+
console.error('Failed to fetch user info:', error);
|
|
35
|
+
setUserInfo(null);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
.catch(() => {
|
|
40
|
+
setAuthStatus('unauthenticated');
|
|
41
|
+
});
|
|
42
|
+
}, []);
|
|
43
|
+
|
|
44
|
+
const value: AuthenticatorContextType = useMemo(
|
|
45
|
+
() => ({ authStatus, authInfo, refresh, userInfo }),
|
|
46
|
+
[authStatus, authInfo, userInfo, refresh]
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
// Refresh auth state on page load
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
refresh();
|
|
52
|
+
}, [refresh]);
|
|
53
|
+
|
|
54
|
+
// Refresh auth state in response to auth events
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
const options = { onSignIn: refresh, onSignOut: refresh };
|
|
57
|
+
return authProvider.registerAuthEventHandler(options);
|
|
58
|
+
}, [refresh]);
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<AuthenticatorContext.Provider value={value}>
|
|
62
|
+
{children}
|
|
63
|
+
</AuthenticatorContext.Provider>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { createContext } from 'react';
|
|
2
|
-
import { AuthStatus } from "../models/auth-status";
|
|
3
|
-
import { UserDetail } from "@cirrobio/api-client";
|
|
4
|
-
import { CurrentUser } from "@cirrobio/sdk";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export type AuthenticatorContextType = {
|
|
9
|
-
authStatus: AuthStatus;
|
|
10
|
-
authInfo: CurrentUser | null;
|
|
11
|
-
userInfo: UserDetail;
|
|
12
|
-
refresh: () => void;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export const AuthenticatorContext = createContext<AuthenticatorContextType>(null);
|
|
1
|
+
import { createContext } from 'react';
|
|
2
|
+
import { AuthStatus } from "../models/auth-status";
|
|
3
|
+
import { UserDetail } from "@cirrobio/api-client";
|
|
4
|
+
import { CurrentUser } from "@cirrobio/sdk";
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
export type AuthenticatorContextType = {
|
|
9
|
+
authStatus: AuthStatus;
|
|
10
|
+
authInfo: CurrentUser | null;
|
|
11
|
+
userInfo: UserDetail;
|
|
12
|
+
refresh: () => void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const AuthenticatorContext = createContext<AuthenticatorContextType>(null);
|
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
import { useCallback, useContext } from 'react';
|
|
2
|
-
import { AuthenticatorContext, AuthenticatorContextType } from './authentication-context';
|
|
3
|
-
import { useAppConfig } from "@cirrobio/react-core";
|
|
4
|
-
|
|
5
|
-
type UseAuthenticator = AuthenticatorContextType & {
|
|
6
|
-
isLoggedIn: boolean;
|
|
7
|
-
signOut: () => void;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Custom hook to access authentication context
|
|
12
|
-
*/
|
|
13
|
-
export function useAuthenticator(): UseAuthenticator {
|
|
14
|
-
const context = useContext(AuthenticatorContext);
|
|
15
|
-
const { authProvider } = useAppConfig();
|
|
16
|
-
|
|
17
|
-
const signOut = useCallback(() => {
|
|
18
|
-
sessionStorage.clear();
|
|
19
|
-
authProvider.signOut().then(() => {
|
|
20
|
-
// Location reload clears any in-memory state
|
|
21
|
-
// Amplify does a hard reload automatically, but only if you are signing in from oauth / SSO
|
|
22
|
-
location.reload();
|
|
23
|
-
});
|
|
24
|
-
}, [authProvider]);
|
|
25
|
-
|
|
26
|
-
return {
|
|
27
|
-
...context,
|
|
28
|
-
isLoggedIn: context?.authStatus === 'authenticated',
|
|
29
|
-
signOut,
|
|
30
|
-
}
|
|
31
|
-
}
|
|
1
|
+
import { useCallback, useContext } from 'react';
|
|
2
|
+
import { AuthenticatorContext, AuthenticatorContextType } from './authentication-context';
|
|
3
|
+
import { useAppConfig } from "@cirrobio/react-core";
|
|
4
|
+
|
|
5
|
+
type UseAuthenticator = AuthenticatorContextType & {
|
|
6
|
+
isLoggedIn: boolean;
|
|
7
|
+
signOut: () => void;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Custom hook to access authentication context
|
|
12
|
+
*/
|
|
13
|
+
export function useAuthenticator(): UseAuthenticator {
|
|
14
|
+
const context = useContext(AuthenticatorContext);
|
|
15
|
+
const { authProvider } = useAppConfig();
|
|
16
|
+
|
|
17
|
+
const signOut = useCallback(() => {
|
|
18
|
+
sessionStorage.clear();
|
|
19
|
+
authProvider.signOut().then(() => {
|
|
20
|
+
// Location reload clears any in-memory state
|
|
21
|
+
// Amplify does a hard reload automatically, but only if you are signing in from oauth / SSO
|
|
22
|
+
location.reload();
|
|
23
|
+
});
|
|
24
|
+
}, [authProvider]);
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
...context,
|
|
28
|
+
isLoggedIn: context?.authStatus === 'authenticated',
|
|
29
|
+
signOut,
|
|
30
|
+
}
|
|
31
|
+
}
|