Skip to content

Commit

Permalink
release: v1.0.3
Browse files Browse the repository at this point in the history
* fix: make sure to always pass config for SSR

* refactor: simplify config sharing

* refactor: make `getCurrentUser` used for CSR only

* chore: update readme

* chore: bump version

* Create big-squids-hug.md
  • Loading branch information
karimdaghari authored Oct 31, 2023
1 parent 126d2de commit 7521d8d
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 103 deletions.
5 changes: 5 additions & 0 deletions .changeset/big-squids-hug.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@marzee/react-auth-amplify": patch
---

v1.0.3
72 changes: 39 additions & 33 deletions packages/react-auth-amplify/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,32 @@

## Usage

1. Create a file called `lib/auth.ts` and export the following object:

```ts
import { type AmplifyAuthConfig as Config } from "@marzee/react-auth-amplify";
export const amplifyAuthConfig: Config = {
Auth: {
identityPoolId: process.env.IDENTITY_POOL_ID,
region: process.env.REGION,
userPoolId: process.env.USER_POOL_ID,
userPoolWebClientId: process.env.USER_POOL_WEB_CLIENT_ID
}
ssr: true // only if you plan on using SSR (with Next.js), otherwise simply omit it.
};

```

1. Wrap your app using `AuthProvider`

```tsx
import { AuthProvider } from "@marzeelabs/react-auth-amplify"
import { AuthProvider } from "@marzeelabs/react-auth-amplify";
import { amplifyAuthConfig } from "lib/auth";

export function AppWrapper() {
return (
<AuthProvider
setup={{
identityPoolId: process.env.IDENTITY_POOL_ID,
region: process.env.REGION,
userPoolId: process.env.USER_POOL_ID,
userPoolWebClientId: process.env.USER_POOL_WEB_CLIENT_ID
}}
config={amplifyAuthConfig}
events={{
signIn: (data) => {
// do what you want after the signIn event has been fired (e.g., redirect to a page)
Expand All @@ -31,7 +43,7 @@ export function AppWrapper() {
}
```

1. Use the `signIn/signOut/etc...` functions in combination with your components to control the authentication flow (see [API](#api) section below for all available functions)
3. Use the `signIn/signOut/etc...` functions in combination with your components to control the authentication flow (see [API](#api) section below for all available functions)

```tsx
import { useForm } from 'react-hook-form';
Expand Down Expand Up @@ -64,7 +76,7 @@ export const SignInComponent = () => {
}
```

3. Get the current user using the `useAuthContext` hook
4. Get the current user using the `useAuthContext` hook

```tsx
import { useAuthContext } from '@marzeelabs/react-auth-amplify';
Expand All @@ -77,7 +89,7 @@ export const Component = () => {

```

4. If you want to extend the type of the `currentUser` you can do so by using module augmentation
5. If you want to extend the type of the `currentUser` you can do so by using module augmentation

```ts
declare module '@marzeelabs/react-auth-amplify' {
Expand All @@ -87,32 +99,25 @@ declare module '@marzeelabs/react-auth-amplify' {
}
```

5. If you to use this package in Next.js:
1. you need to set the `ssr` option to `true` in the `AuthProvider` component (see below)
6. If you to use this package in Next.js:
1. you need to set the `ssr` option to `true` in the `lib/auth.ts` file (see below)
2. Use the `getServerAuth` function to get the current user in the `getServerSideProps` function of your page

```tsx
import { AuthProvider } from "@marzeelabs/react-auth-amplify"

export function AppWrapper() {
return (
<AuthProvider
setup={{
ssr: true, // by default this is set to false, if you need auth in SSR (with Next.js) set this to true
identityPoolId: process.env.IDENTITY_POOL_ID,
region: process.env.REGION,
userPoolId: process.env.USER_POOL_ID,
userPoolWebClientId: process.env.USER_POOL_WEB_CLIENT_ID
}}
events={{
signIn: (data) => {
// do what you want after the signIn event has been fired (e.g., redirect to a page)
}
}}>
<App />
</AuthProvider>
)
}
// your other imports...
import { amplifyAuthConfig } from "lib/auth";

export const getServerSideProps: GetServerSideProps = async (ctx) => {
const currentUser = await getServerAuth({
req: ctx.req,
config: amplifyAuthConfig
});
return {
props: {}
};
};

// the rest of your page...
```

## API
Expand Down Expand Up @@ -140,6 +145,7 @@ The following functions are available:
| `updateUserAttributes` | Updates the current user's attributes in AWS Cognito using the provided partial user attributes. |
| `deleteUserAttributes` | Deletes the specified attributes from the current user's attributes in AWS Cognito. |
| `deleteUser` | Deletes the current user from AWS Cognito. |
| `getServerAuth` | Returns the currently authenticated user in an SSR context. |

## Install

Expand Down
2 changes: 1 addition & 1 deletion packages/react-auth-amplify/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@marzee/react-auth-amplify",
"version": "1.0.2",
"version": "1.0.3",
"description": "A React.js-Amplify Auth integration",
"repository": {
"type": "git",
Expand Down
3 changes: 2 additions & 1 deletion packages/react-auth-amplify/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ export {
deleteUser,
deleteUserAttributes,
forgotPassword,
getServerAuth,
signIn,
signOut,
signUp,
updateUserAttributes,
type UserAttributes
} from './lib/functions';
export { getServerAuth } from './lib/next';
export * from './react/provider';
export type { AmplifyAuthConfig } from './types/amplify';
25 changes: 7 additions & 18 deletions packages/react-auth-amplify/src/lib/functions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Auth, withSSRContext } from 'aws-amplify';
import type { GetServerSidePropsContext } from 'next';
import { Auth } from 'aws-amplify';

type CognitoDefaults = {
/**
Expand Down Expand Up @@ -28,7 +27,7 @@ type DefaultUserAttributes = CognitoDefaults & {
*/
export interface UserAttributes extends DefaultUserAttributes {}

type FunctionOutput<TData = unknown, TError = unknown> =
export type FunctionOutput<TData = unknown, TError = unknown> =
| {
status: 'SUCCESS';
data: TData;
Expand Down Expand Up @@ -348,15 +347,14 @@ export type CognitoUser = Record<string, unknown> & {
type CurrentAuthenticatedUserReturn = CognitoUser | undefined | null;
/**
* @internal This function returns the currently authenticated user. It is only meant for internal use.
* @param auth This is the Auth object that is returned from `withSSRContext`.
*/
export async function getCurrentUser(
auth?: typeof Auth
): Promise<FunctionOutput<CurrentAuthenticatedUserReturn>> {
export async function getCurrentUser(): Promise<
FunctionOutput<CurrentAuthenticatedUserReturn>
> {
try {
const authClass = auth ?? Auth;
const getCurrentAuthenticatedUser = authClass.currentAuthenticatedUser;
const res: CurrentAuthenticatedUserReturn =
await getCurrentAuthenticatedUser();
await Auth.currentAuthenticatedUser();
return {
status: 'SUCCESS',
data: res,
Expand Down Expand Up @@ -451,12 +449,3 @@ export async function deleteUser(): Promise<
};
}
}

/**
* @description This is a next.js specific option, it is to be used ONLY inside `getServerSideProps`
*/
export async function getServerAuth(ctx: GetServerSidePropsContext) {
const amplify = withSSRContext({ req: ctx.req });
const auth: typeof Auth = amplify.Auth;
return await getCurrentUser(auth);
}
44 changes: 44 additions & 0 deletions packages/react-auth-amplify/src/lib/next.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { withSSRContext, type Auth } from 'aws-amplify';
import type { GetServerSidePropsContext } from 'next';
import type { AmplifyAuthConfig } from '../types/amplify';
import { type FunctionOutput } from './functions';

type CognitoUserSSR = {
username: string;
} & Record<string, unknown>;

type CurrentAuthenticatedUserReturnSSR = CognitoUserSSR | undefined | null;

type Params = {
req: GetServerSidePropsContext['req'];
config: AmplifyAuthConfig;
};
/**
* @summary This is a next.js specific option, it is to be used ONLY inside `getServerSideProps`
* @description In SSR, Amplify creates a new instance scoped/per-request, and as such you must auth config every time you want to get the current user on the server side.
* @see https://docs.amplify.aws/lib/ssr/q/platform/js/#withssrcontext
*/
export async function getServerAuth({
req,
config
}: Params): Promise<FunctionOutput<CurrentAuthenticatedUserReturnSSR>> {
if (!config) throw new Error('No config provided for getServerAuth');
const Amplify = withSSRContext({ req });
Amplify.configure(config);
const auth: typeof Auth = Amplify.Auth;
try {
const data: CurrentAuthenticatedUserReturnSSR =
await auth.currentAuthenticatedUser();
return {
status: 'SUCCESS',
data,
err: null
};
} catch (err) {
return {
status: 'ERROR',
data: null,
err
};
}
}
48 changes: 4 additions & 44 deletions packages/react-auth-amplify/src/lib/setup.ts
Original file line number Diff line number Diff line change
@@ -1,53 +1,13 @@
import { Amplify } from 'aws-amplify';
import { useEffect } from 'react';
import type { AWSAmplifyConfig } from '../types/amplify';

type CommonProps = {
identityPoolId: string;
region: string;
userPoolId: string;
userPoolWebClientId: string;
/**
* This is a next.js specific option
* @default false
*/
ssr?: boolean;
};

type PropsWithSSR = CommonProps & {
/**
* @summary This should be set to the domain of the app (optional)
* @description You should set this to the (production) domain of the app. Note: You only need to set this on production env, not on development/localhost.
* @example '.example.com'
* @see https://docs.amplify.aws/lib/auth/getting-started/q/platform/js/#set-up-and-configure-amplify-auth
*/
cookieDomain?: string;
ssr: true;
};

type PropsWithoutSSR = CommonProps & {
cookieDomain?: undefined;
ssr?: false;
};

export type SetupProps = PropsWithSSR | PropsWithoutSSR;
import type { AmplifyAuthConfig } from '../types/amplify';

/**
* @summary This is a hook that sets up Amplify
*/
export function useSetupAmplify({ cookieDomain, ...props }: SetupProps) {
export function useSetupAmplify(config: AmplifyAuthConfig) {
useEffect(() => {
Amplify.configure({
Auth: {
...props,
cookieStorage: {
domain: cookieDomain,
/**
* Although Chrome/FF allow cookies to be set on localhost, Safari does not.
*/
secure: process.env.NODE_ENV === 'production'
}
}
} satisfies AWSAmplifyConfig);
if (!config) throw new Error('Missing Amplify config for client-side');
Amplify.configure(config);
}, []);
}
14 changes: 9 additions & 5 deletions packages/react-auth-amplify/src/react/provider.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { Hub } from 'aws-amplify';
import React from 'react';
import { getCurrentUser, type CognitoUser } from '../lib/functions';
import { useSetupAmplify, type SetupProps } from '../lib/setup';
import type { AuthEventType, HubEventHandler } from '../types/amplify';
import { useSetupAmplify } from '../lib/setup';
import type {
AmplifyAuthConfig,
AuthEventType,
HubEventHandler
} from '../types/amplify';

type CurrentUser = CognitoUser['attributes'];

Expand Down Expand Up @@ -32,13 +36,13 @@ type AuthProviderProps = {
userPoolWebClientId: process.env.USER_POOL_WEB_CLIENT_ID
}
*/
setup: SetupProps;
config: AmplifyAuthConfig;
} & Partial<EventsTypes>;

type Props = React.PropsWithChildren<AuthProviderProps>;

export function AuthProvider({ children, events, setup }: Props) {
useSetupAmplify(setup);
export function AuthProvider({ children, events, config }: Props) {
useSetupAmplify(config);

const [currentUser, setCurrentUser] = React.useState<CurrentUser | null>(
null
Expand Down
2 changes: 1 addition & 1 deletion packages/react-auth-amplify/src/types/amplify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ type AuthConfig = ReturnType<typeof Auth.configure>;
/**
* Configuration for AWS Amplify.
*/
export type AWSAmplifyConfig = {
export type AmplifyAuthConfig = {
/**
* Configuration for AWS Amplify Auth.
*/
Expand Down

0 comments on commit 7521d8d

Please sign in to comment.