Skip to content

Instantly share code, notes, and snippets.

@aquiseb
Last active March 14, 2023 11:16
Show Gist options
  • Save aquiseb/1b12fce71168a8ecb4532c7c8747d885 to your computer and use it in GitHub Desktop.
Save aquiseb/1b12fce71168a8ecb4532c7c8747d885 to your computer and use it in GitHub Desktop.

Nextjs Authentication with AWS Amplify

Install AWS Amplify @preview

yarn add aws-amplify@preview

# Optional
yarn add hoist-non-react-statics

Create a HOC that will be used in _app.js

// libs/withAmplify.js
import * as React from "react";
import { Amplify, withSSRContext } from "aws-amplify";
import hoistNonReactStatics from "hoist-non-react-statics";

const AmplifyContext = React.createContext();
export const useAmplify = () => React.useContext(AmplifyContext);

export const withAmplify = (ComposedComponent) => {
	const WithAmplify = (props) => {
		const { amplifyConfig, ...rest } = props;
		const { Auth } = withSSRContext();

		React.useMemo(() => {
			Amplify.configure(amplifyConfig);
			Auth.configure(amplifyConfig);
		}, []);

		return (
			<AmplifyContext.Provider value={{ amplifyConfig }}>
				<ComposedComponent {...rest} />
			</AmplifyContext.Provider>
		);
	};

	WithAmplify.getInitialProps = async (appContext) => {
		const { Auth } = withSSRContext(appContext.ctx);
		const amplifyConfig = {
			Auth: {
				region: process.env.AWS_AMPLIFY_REGION,
				userPoolId: process.env.AWS_AMPLIFY_USER_POOL_ID,
				userPoolWebClientId: process.env.AWS_AMPLIFY_USER_POOL_WEB_CLIENT_ID,
				mandatorySignIn: true,
				authenticationFlowType: process.env.AWS_AMPLIFY_AUTHENTICATION_FLOW_TYPE,
				oauth: {
					domain: process.env.AWS_AMPLIFY_OAUTH_DOMAIN,
					scope: process.env.AWS_AMPLIFY_OAUTH_SCOPE,
					redirectSignIn: process.env.AWS_AMPLIFY_OAUTH_REDIRECT_SIGN_IN,
					redirectSignOut: process.env.AWS_AMPLIFY_OAUTH_REDIRECT_SIGN_OUT,
					responseType: process.env.AWS_AMPLIFY_OAUTH_RESPONSE_TYPE,
				},
			},
		};

		if (typeof window === 'undefined') {
			Amplify.configure(amplifyConfig);
			Auth.configure(amplifyConfig);
		}

		return {
			amplifyConfig,
		};
	};

	return hoistNonReactStatics(WithAmplify, ComposedComponent);
};

Use withAmplify in _app.js

Wrap your App with withAmplify HOC previously created.

// pages/_app.js
import * as React from "react";
import { withAmplify } from "../libs/withAmplify";

const MyApp = ({ Component, pageProps }) => {
	return <Component {...pageProps} />;
};

export default withAmplify(MyApp);

Create a login page

// pages/login.js
import * as React from "react";
import { withSSRContext } from "aws-amplify";
import Router from "next/router";

const Login = (props) => {
	const [state, _setState] = React.useState({
		username: "",
		password: "",
	});
	const setState = (newState) => _setState({ ...state, ...newState });

	// 👇 Your frontend code is now "SSR-aware", giving the backend access to the current session
	const { Auth } = withSSRContext();

	React.useEffect(() => {
		(async () => {
			let user, currentSession;
			try {
				user = await Auth.currentAuthenticatedUser();
			} catch (e) {
				console.error("currentAuthenticatedUser error:", e);
			}

			try {
				currentSession = await Auth.currentSession();
			} catch (e) {
				console.error("currentSession error:", e);
			}

			// if (currentSession) Router.push("/");
			console.log("login.user --", user);
			console.log("login.currentSession --", currentSession);
		})();
	}, []);

	const handleChange = (e) => {
		setState({ [e.target.id]: e.target.value });
	};

	const federatedSignIn = () => {
		Auth.federatedSignIn();
	};

	const signIn = async (e) => {
		e.preventDefault();
		try {
			await Auth.signIn(state.username, state.password);
			Router.push("/");
		} catch (err) {
			if (err.code === "InvalidParameterException" || err.code === "NotAuthorizedException") alert("Incorrect username or password.");
			else alert(err.message);
		}
	};

	return (
		<>
			<h1>Login page</h1>
			<form onSubmit={signIn}>
				<input id="username" type="text" value={state.username} onChange={handleChange} />
				<input id="password" type="password" value={state.password} onChange={handleChange} />
				<button type="submit" onClick={signIn}>
					Sign in
				</button>
			</form>
			<br />
			<button onClick={federatedSignIn}>Hosted UI</button>
		</>
	);
};

export default Login;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment