-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4 from aversini/feat-AuthProvider-MVP
feat: AuthProvider MVP
- Loading branch information
Showing
20 changed files
with
10,127 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# @versini/auth-provider | ||
|
||
This package provides a simple authentication provider for your application. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
export default { | ||
report: { | ||
previous: "stats/stats.json", | ||
current: "tmp/stats.json", | ||
}, | ||
sizes: [ | ||
/** | ||
* JavaScript static assets. | ||
*/ | ||
{ | ||
path: "dist/index.js", | ||
limit: "3 kb", | ||
}, | ||
], | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
{ | ||
"name": "@versini/auth-provider", | ||
"version": "0.0.0", | ||
"license": "MIT", | ||
"author": "Arno Versini", | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"homepage": "https://github.com/aversini/auth-client", | ||
"repository": { | ||
"type": "git", | ||
"url": "[email protected]:aversini/auth-client.git" | ||
}, | ||
"type": "module", | ||
"main": "dist/index.js", | ||
"types": "dist/index.d.ts", | ||
"files": ["dist"], | ||
"scripts": { | ||
"build:check": "tsc", | ||
"build:js": "vite build", | ||
"build:types": "tsup", | ||
"build": "npm-run-all --serial clean build:check build:js build:types", | ||
"clean": "rimraf dist tmp", | ||
"dev:js": "vite build --watch --mode development", | ||
"dev:types": "tsup --watch src", | ||
"dev": "npm-run-all clean --parallel dev:js dev:types", | ||
"lint": "biome lint src", | ||
"start": "static-server dist --port 5173", | ||
"stats:pr": "bundlesize -c bundlesize.config.js -p \"$npm_package_version\" -o tmp/stats.json --silent", | ||
"stats:release": "bundlesize -c bundlesize.config.js -p \"$npm_package_version\" -o stats/stats.json --silent", | ||
"stats:report": "bundlesize -c bundlesize.config.js --type report -o tmp/pr-stats.md --silent", | ||
"stats": "bundlesize -c bundlesize.config.js -p \"$npm_package_version\"", | ||
"test:watch": "vitest", | ||
"test": "vitest run" | ||
}, | ||
"peerDependencies": { | ||
"react": "^18.3.1", | ||
"react-dom": "^18.3.1" | ||
}, | ||
"devDependencies": { | ||
"react": "18.3.1", | ||
"react-dom": "18.3.1" | ||
}, | ||
"dependencies": { | ||
"@versini/ui-hooks": "3.0.0", | ||
"uuid": "10.0.0" | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
packages/auth-provider/src/components/AuthProvider/AuthContext.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { createContext } from "react"; | ||
|
||
export type AuthContextType = { | ||
login: (username: string, password: string) => Promise<boolean>; | ||
logout: () => void; | ||
isAuthenticated: boolean; | ||
accessToken?: string; | ||
refreshToken?: string; | ||
idToken?: string; | ||
logoutReason?: string; | ||
}; | ||
|
||
const stub = (): never => { | ||
throw new Error("You forgot to wrap your component in <AuthProvider>."); | ||
}; | ||
|
||
export const AuthContext = createContext<AuthContextType>({ | ||
isAuthenticated: false, | ||
login: stub, | ||
logout: stub, | ||
accessToken: undefined, | ||
refreshToken: undefined, | ||
idToken: undefined, | ||
logoutReason: "", | ||
}); |
159 changes: 159 additions & 0 deletions
159
packages/auth-provider/src/components/AuthProvider/AuthProvider.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
import { useLocalStorage } from "@versini/ui-hooks"; | ||
import { useEffect, useRef, useState } from "react"; | ||
import { v4 as uuidv4 } from "uuid"; | ||
|
||
import { AuthContext } from "./AuthContext"; | ||
|
||
type AuthState = { | ||
isAuthenticated: boolean; | ||
idToken: string; | ||
logoutReason: string; | ||
userId: string; | ||
accessToken?: string; | ||
refreshToken?: string; | ||
}; | ||
export const AUTH_TYPES = { | ||
ID_TOKEN: "id_token", | ||
}; | ||
const EXPIRED_SESSION = | ||
"Oops! It looks like your session has expired. For your security, please log in again to continue."; | ||
|
||
const serviceCall = async ({ params = {} }: { params?: any }) => { | ||
try { | ||
const nonce = uuidv4(); | ||
const response = await fetch( | ||
`${process.env.PUBLIC_AUTH_SERVER_URL}/authenticate`, | ||
{ | ||
credentials: "include", | ||
method: "POST", | ||
headers: { | ||
"Content-Type": "application/json", | ||
"X-Auth-TenantId": `${params.tenantId}`, | ||
}, | ||
body: JSON.stringify({ ...params, nonce }), | ||
}, | ||
); | ||
|
||
if (response.status !== 200) { | ||
return { status: response.status, data: [] }; | ||
} | ||
const { data, errors } = await response.json(); | ||
if (data.nonce !== nonce) { | ||
return { status: 500, data: [] }; | ||
} | ||
|
||
return { | ||
status: response.status, | ||
data, | ||
errors, | ||
}; | ||
} catch (_error) { | ||
console.error(_error); | ||
return { status: 500, data: [] }; | ||
} | ||
}; | ||
|
||
function usePrevious<T>(state: T): T | undefined { | ||
const ref = useRef<T>(); | ||
useEffect(() => { | ||
ref.current = state; | ||
}); | ||
return ref.current; | ||
} | ||
|
||
export const AuthProvider = ({ | ||
children, | ||
sessionExpiration, | ||
tenantId, | ||
accessType, | ||
}: { | ||
children: React.ReactNode; | ||
sessionExpiration?: string; | ||
tenantId: string; | ||
accessType?: string; | ||
}) => { | ||
const [accessToken, setAccessToken, removeAccessToken] = useLocalStorage( | ||
`@@auth@@::${tenantId}::@@access@@`, | ||
"", | ||
); | ||
const [refreshToken, setRefreshToken, removeRefreshToken] = useLocalStorage( | ||
`@@auth@@::${tenantId}::@@refresh@@`, | ||
"", | ||
); | ||
const [idToken, setIdToken, removeIdToken] = useLocalStorage( | ||
`@@auth@@::${tenantId}::@@user@@`, | ||
"", | ||
); | ||
const [authState, setAuthState] = useState<AuthState>({ | ||
isAuthenticated: !!idToken, | ||
accessToken, | ||
refreshToken, | ||
idToken, | ||
logoutReason: "", | ||
userId: "", | ||
}); | ||
|
||
const previousIdToken = usePrevious(idToken) || ""; | ||
|
||
useEffect(() => { | ||
if (previousIdToken !== idToken && idToken !== "") { | ||
setAuthState({ | ||
isAuthenticated: true, | ||
accessToken, | ||
refreshToken, | ||
idToken, | ||
logoutReason: "", | ||
userId: authState.userId, | ||
}); | ||
} else if (previousIdToken !== idToken && idToken === "") { | ||
setAuthState({ | ||
isAuthenticated: false, | ||
accessToken: "", | ||
refreshToken: "", | ||
idToken: "", | ||
logoutReason: EXPIRED_SESSION, | ||
userId: "", | ||
}); | ||
} | ||
}, [accessToken, refreshToken, idToken, previousIdToken, authState.userId]); | ||
|
||
const login = async (username: string, password: string) => { | ||
const response = await serviceCall({ | ||
params: { | ||
type: accessType || AUTH_TYPES.ID_TOKEN, | ||
username, | ||
password, | ||
sessionExpiration, | ||
tenantId, | ||
}, | ||
}); | ||
|
||
if (response.data?.idToken) { | ||
setIdToken(response.data.idToken); | ||
response.data.accessToken && setAccessToken(response.data.accessToken); | ||
response.data.refreshToken && setRefreshToken(response.data.refreshToken); | ||
setAuthState({ | ||
isAuthenticated: true, | ||
idToken: response.data.idToken, | ||
accessToken: response.data.accessToken, | ||
refreshToken: response.data.refreshToken, | ||
userId: response.data.userId, | ||
logoutReason: "", | ||
}); | ||
return true; | ||
} | ||
return false; | ||
}; | ||
|
||
const logout = () => { | ||
removeAccessToken(); | ||
removeRefreshToken(); | ||
removeIdToken(); | ||
}; | ||
|
||
return ( | ||
<AuthContext.Provider value={{ ...authState, login, logout }}> | ||
{children} | ||
</AuthContext.Provider> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export { AuthProvider } from "./AuthProvider"; | ||
export { useAuth } from "./useAuth"; |
5 changes: 5 additions & 0 deletions
5
packages/auth-provider/src/components/AuthProvider/useAuth.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { useContext } from "react"; | ||
import { AuthContext, type AuthContextType } from "./AuthContext"; | ||
|
||
export const useAuth = (context = AuthContext): AuthContextType => | ||
useContext(context) as AuthContextType; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export { AuthProvider } from "./AuthProvider/AuthProvider"; | ||
export { useAuth } from "./AuthProvider/useAuth"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/// <reference types="vite/client" /> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
{ | ||
"0.0.0": { | ||
"dist/index.js": { | ||
"fileSize": 6711, | ||
"fileSizeGzip": 2552, | ||
"limit": "3 kb", | ||
"passed": true | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
{ | ||
"compilerOptions": { | ||
"target": "ES2020", | ||
"useDefineForClassFields": true, | ||
"lib": ["ES2020", "DOM", "DOM.Iterable"], | ||
"module": "ESNext", | ||
"skipLibCheck": true, | ||
"types": ["vitest/globals", "@testing-library/jest-dom"], | ||
|
||
/* Bundler mode */ | ||
"moduleResolution": "bundler", | ||
"allowImportingTsExtensions": true, | ||
"resolveJsonModule": true, | ||
"isolatedModules": true, | ||
"noEmit": true, | ||
"jsx": "react-jsx", | ||
|
||
/* Linting */ | ||
"strict": true, | ||
"noUnusedLocals": true, | ||
"noUnusedParameters": true, | ||
"noFallthroughCasesInSwitch": true | ||
}, | ||
"include": ["src"], | ||
"references": [{ "path": "./tsconfig.node.json" }] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
{ | ||
"compilerOptions": { | ||
"composite": true, | ||
"skipLibCheck": true, | ||
"module": "ESNext", | ||
"moduleResolution": "bundler", | ||
"allowSyntheticDefaultImports": true | ||
}, | ||
"include": ["./vite.config.ts", "./vitest.setup.ts"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { defineConfig } from "tsup"; | ||
|
||
export default defineConfig({ | ||
format: "esm", | ||
entry: { | ||
index: "src/components/index.ts", | ||
}, | ||
outDir: "dist", | ||
dts: { | ||
only: true, | ||
}, | ||
}); |
Oops, something went wrong.