Skip to content

Commit

Permalink
feat: extract AuthProvider from AuthContext and add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
amalv committed Jan 7, 2024
1 parent e85ece5 commit 6559550
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 58 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"preview": "vite preview",
"semantic-release": "semantic-release",
"test": "vitest",
"test:staged": "CI=true npm test",
"prepare": "husky install"
},
"dependencies": {
Expand Down Expand Up @@ -57,6 +58,6 @@
},
"lint-staged": {
"*.{ts,tsx}": "eslint --fix",
"*test.{ts,tsx}": "npm run test"
"*test.{ts,tsx}": "npm run test:staged"
}
}
2 changes: 1 addition & 1 deletion src/App.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ vi.mock("@auth0/auth0-react", () => ({
Auth0Provider: ({ children }: { children: ReactNode }) => children,
}));

vi.mock("./contexts/AuthContext", () => ({
vi.mock("./contexts/AuthProvider", () => ({
AuthProvider: ({ children }: { children: ReactNode }) => children,
}));

Expand Down
58 changes: 2 additions & 56 deletions src/contexts/AuthContext.tsx
Original file line number Diff line number Diff line change
@@ -1,66 +1,12 @@
import { createContext, useState, useEffect, useMemo } from "react";
import { IdToken, useAuth0, User } from "@auth0/auth0-react";
import { jwtDecode } from "jwt-decode";

interface DecodedToken {
exp: number;
iat: number;
iss: string;
sub: string;
aud: string;
}
import { createContext } from "react";
import { User } from "@auth0/auth0-react";

interface AuthContextProps {
token: string | null;
user: User | undefined;
}

interface AuthProviderProps {
children: React.ReactNode;
}

export const AuthContext = createContext<AuthContextProps>({
token: null,
user: undefined,
});

const refreshToken = (
getIdTokenClaims: () => Promise<IdToken | undefined>,
setToken: (token: string | null) => void
) => {
getIdTokenClaims().then((claims) => {
if (claims) {
const idToken = claims.__raw; // The raw id_token
localStorage.setItem("auth0.token", idToken);
setToken(idToken);

try {
const decodedToken: DecodedToken = jwtDecode(idToken);
const expiryTime = decodedToken.exp * 1000; // Convert to milliseconds
const timeoutDuration = expiryTime - Date.now() - 60 * 1000; // Refresh 1 minute before expiry

setTimeout(
() => refreshToken(getIdTokenClaims, setToken),
timeoutDuration
);
} catch (error) {
console.error("Invalid token", error);
}
}
});
};

export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
const { user, getIdTokenClaims } = useAuth0();
const [token, setToken] = useState<string | null>(null);

useEffect(() => {
if (user && getIdTokenClaims) {
refreshToken(getIdTokenClaims, setToken);
}
}, [user, getIdTokenClaims]);

const value = useMemo(() => ({ token, user }), [token, user]);

return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
};
29 changes: 29 additions & 0 deletions src/contexts/AuthProvider.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { render, act, screen } from "@testing-library/react";
import { AuthProvider } from "./AuthProvider";
import { useAuth0 } from "@auth0/auth0-react";
import { vi } from "vitest";

vi.mock("@auth0/auth0-react", () => ({
useAuth0: vi.fn().mockReturnValue({
user: { name: "Test User" },
getIdTokenClaims: vi.fn().mockResolvedValue({
__raw:
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
}),
}),
}));

describe("AuthProvider", () => {
it("provides auth context", async () => {
await act(async () => {
render(
<AuthProvider>
<div>Test</div>
</AuthProvider>
);
});

expect(screen.getByText("Test")).toBeInTheDocument();
expect(useAuth0().getIdTokenClaims).toHaveBeenCalled();
});
});
56 changes: 56 additions & 0 deletions src/contexts/AuthProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { useState, useEffect, useMemo } from "react";
import { IdToken, useAuth0 } from "@auth0/auth0-react";
import { jwtDecode } from "jwt-decode";
import { AuthContext } from "./AuthContext";

interface DecodedToken {
exp: number;
iat: number;
iss: string;
sub: string;
aud: string;
}

interface AuthProviderProps {
children: React.ReactNode;
}
const refreshToken = async (
getIdTokenClaims: () => Promise<IdToken | undefined>,
setToken: (token: string | null) => void
) => {
if (getIdTokenClaims) {
const claims = await getIdTokenClaims();
if (claims) {
const idToken = claims.__raw; // The raw id_token
localStorage.setItem("auth0.token", idToken);
setToken(idToken);

try {
const decodedToken: DecodedToken = jwtDecode(idToken);
const expiryTime = decodedToken.exp * 1000; // Convert to milliseconds
const timeoutDuration = expiryTime - Date.now() - 60 * 1000; // Refresh 1 minute before expiry

setTimeout(() => {
refreshToken(getIdTokenClaims, setToken);
}, timeoutDuration);
} catch (error) {
console.error("Invalid token", error);
}
}
}
};

export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
const { user, getIdTokenClaims } = useAuth0();
const [token, setToken] = useState<string | null>(null);

useEffect(() => {
if (user && getIdTokenClaims) {
refreshToken(getIdTokenClaims, setToken);
}
}, [user, getIdTokenClaims]);

const value = useMemo(() => ({ token, user }), [token, user]);

return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
};
1 change: 1 addition & 0 deletions src/contexts/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from "./AuthContext";
export * from "./AuthProvider";
export * from "./useAuth";
2 changes: 2 additions & 0 deletions vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ export default defineConfig({
"**/index.ts",
"**/tests/**",
"**/tests-examples/**",
"/playwrightconfig.ts",
"**/types.ts",
],
},
},
Expand Down

0 comments on commit 6559550

Please sign in to comment.