amplify-ui icon indicating copy to clipboard operation
amplify-ui copied to clipboard

FR(Authenticator): Support rendering Authenticator before calling Amplify.configure

Open dannymas-amazon opened this issue 1 year ago • 1 comments

Before creating a new issue, please confirm:

On which framework/platform are you having an issue?

React

Which UI component?

Authenticator

How is your app built?

Vite

What browsers are you seeing the problem on?

Chrome, Firefox, Microsoft Edge, Safari

Which region are you seeing the problem in?

us-east-1

Please describe your bug.

At the entry point of my app, I am performing a fetch call to get configuration values, then manually performing Amplify.configure. After the fetch call I am creating the root and loading the Authenticator component. This is creating a race condition which can be solved by awaiting the fetch call before creating the root or by adding the createRoot in the then after the fetch call.

However, without the fix, the authenticator loads before amplify is configured. Then when you attempt to login, behind the scenes the authenticator checks if a user is logged in (Which there is) and it returns the error There is already a signed in user. and doesnt reload the component. This cause the site to be stuck at the authenticator and there is no way to get around it. Reloading will load the whole app recreating the problem.

I have two potential suggestions for handling this use case.

  1. Reloading the authenticator component once the first time amplify.configure is called. This needs to be thought through because there could be potential security concerns around this.
  2. When handling that specific error, reload the component. That error is indicative that there we are succesfully connecting to cognito and that someone is currently signed in according to the session and auth data, which should just bypass the authenticator anyway.

What's the expected behaviour?

I would expect when I click sign in, that if there is already a user signed in, it would load the application

Help us reproduce the bug!

In order to reproduce, create a race condition between when you configure amplify and when you load the authenticator. You can use the example code below. You can also just do create a short timeout before doing your normal amplify configure which should reproduce the same results.

Code Snippet

In main.jsx

fetch(`${basePath}/api/amplify-config`).then(async (response) => {
    const amplifyConfig = await response.json();
    Amplify.configure({
        Auth: {
            Cognito: {
                userPoolClientId: amplifyConfig.appClientId,
                userPoolId: amplifyConfig.userPoolId,
                identityPoolId: amplifyConfig.identityPoolId
            }
        },
        Storage: {
            S3: {
                region: amplifyConfig.region,
                bucket: amplifyConfig.uploadBucket
            }
        },
        API: {
            REST: {
                api: {
                    endpoint: basePath,
                    region: amplifyConfig.region,
                },
                headers: async () => {
                    return {
                        Authorization: `Bearer ${(await fetchAuthSession())
                            .tokens?.idToken?.toString()}`,
                    }
                }
            },
        },
    }, {
        API: {
            REST: {
                headers: async (options) => {
                    return {
                        Authorization: `Bearer ${(await fetchAuthSession())
                            .tokens?.idToken?.toString()}`,
                    }
                }
            }
        }
    });
})

ReactDOM.createRoot(document.getElementById('root')).render(
    <React.StrictMode>
        <Authenticator.Provider>
            <App/>
        </Authenticator.Provider>
    </React.StrictMode>
)

In App.jsx

export default function App() {
    const { authStatus, signOut, user } = useAuthenticator((context) => [context.user, context.authStatus]);

    useEffect(() => {
    }, [authStatus])

    return (
        <>
            {authStatus !== "authenticated" || user === undefined ? <Authenticator/> :
                <Suspense fallback={<Loader/>}>
                    <Router user={user} signOut={() => signOut()}/>
                </Suspense>}
        </>
    )
}

Console log output

No response

Additional information and screenshots

Screenshot 2024-03-05 at 1 53 54 PM

dannymas-amazon avatar Mar 05 '24 18:03 dannymas-amazon

@dannymas-amazon Thanks for opening this ✅

calebpollman avatar Mar 06 '24 06:03 calebpollman