@orsetra/shared-auth 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SessionProvider.tsx +86 -0
- package/hooks/useSession.ts +72 -0
- package/index.ts +5 -1
- package/package.json +3 -1
- package/services/zitadel.auth.service.ts +11 -1
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { createContext, useContext, useEffect, useState, ReactNode } from "react"
|
|
4
|
+
|
|
5
|
+
interface SessionUser {
|
|
6
|
+
access_token: string
|
|
7
|
+
id_token?: string
|
|
8
|
+
expires_at?: number
|
|
9
|
+
profile: {
|
|
10
|
+
sub: string
|
|
11
|
+
email?: string
|
|
12
|
+
name?: string
|
|
13
|
+
preferred_username?: string
|
|
14
|
+
[key: string]: any
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface SessionContextType {
|
|
19
|
+
user: SessionUser | null
|
|
20
|
+
accessToken: string | null
|
|
21
|
+
isLoading: boolean
|
|
22
|
+
isAuthenticated: boolean
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const SessionContext = createContext<SessionContextType | undefined>(undefined)
|
|
26
|
+
|
|
27
|
+
interface SessionProviderProps {
|
|
28
|
+
children: ReactNode
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Provider de session pour les micro-apps
|
|
33
|
+
* À utiliser après validation par le middleware
|
|
34
|
+
* Lit directement depuis localStorage (pas de configuration requise)
|
|
35
|
+
*/
|
|
36
|
+
export function SessionProvider({ children }: SessionProviderProps) {
|
|
37
|
+
const [user, setUser] = useState<SessionUser | null>(null)
|
|
38
|
+
const [isLoading, setIsLoading] = useState(true)
|
|
39
|
+
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
const authority = process.env.NEXT_PUBLIC_ZITADEL_AUTHORITY
|
|
42
|
+
const clientId = process.env.NEXT_PUBLIC_ZITADEL_CLIENT_ID
|
|
43
|
+
const storageKey = `oidc.user:${authority}:${clientId}`
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
const userDataString = localStorage.getItem(storageKey)
|
|
47
|
+
|
|
48
|
+
if (userDataString) {
|
|
49
|
+
const userData = JSON.parse(userDataString) as SessionUser
|
|
50
|
+
const isExpired = userData.expires_at && userData.expires_at * 1000 < Date.now()
|
|
51
|
+
|
|
52
|
+
if (!isExpired && userData.access_token) {
|
|
53
|
+
setUser(userData)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
} catch (error) {
|
|
57
|
+
console.error('Error reading session:', error)
|
|
58
|
+
} finally {
|
|
59
|
+
setIsLoading(false)
|
|
60
|
+
}
|
|
61
|
+
}, [])
|
|
62
|
+
|
|
63
|
+
const value: SessionContextType = {
|
|
64
|
+
user,
|
|
65
|
+
accessToken: user?.access_token ?? null,
|
|
66
|
+
isLoading,
|
|
67
|
+
isAuthenticated: !!user,
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<SessionContext.Provider value={value}>
|
|
72
|
+
{children}
|
|
73
|
+
</SessionContext.Provider>
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Hook pour accéder au contexte de session dans les micro-apps
|
|
79
|
+
*/
|
|
80
|
+
export function useSession(): SessionContextType {
|
|
81
|
+
const context = useContext(SessionContext)
|
|
82
|
+
if (context === undefined) {
|
|
83
|
+
throw new Error("useSession must be used within a SessionProvider")
|
|
84
|
+
}
|
|
85
|
+
return context
|
|
86
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState } from 'react'
|
|
4
|
+
|
|
5
|
+
interface SessionUser {
|
|
6
|
+
access_token: string
|
|
7
|
+
id_token?: string
|
|
8
|
+
expires_at?: number
|
|
9
|
+
profile: {
|
|
10
|
+
sub: string
|
|
11
|
+
email?: string
|
|
12
|
+
name?: string
|
|
13
|
+
preferred_username?: string
|
|
14
|
+
[key: string]: any
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface SessionState {
|
|
19
|
+
user: SessionUser | null
|
|
20
|
+
isLoading: boolean
|
|
21
|
+
isAuthenticated: boolean
|
|
22
|
+
accessToken: string | null
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Hook pour récupérer la session utilisateur dans les micro-apps
|
|
27
|
+
* Lit depuis localStorage (partagé via même domaine)
|
|
28
|
+
*/
|
|
29
|
+
export function useSession(): SessionState {
|
|
30
|
+
const [state, setState] = useState<SessionState>({
|
|
31
|
+
user: null,
|
|
32
|
+
isLoading: true,
|
|
33
|
+
isAuthenticated: false,
|
|
34
|
+
accessToken: null,
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
const authority = process.env.NEXT_PUBLIC_ZITADEL_AUTHORITY
|
|
39
|
+
const clientId = process.env.NEXT_PUBLIC_ZITADEL_CLIENT_ID
|
|
40
|
+
const storageKey = `oidc.user:${authority}:${clientId}`
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
const userDataString = localStorage.getItem(storageKey)
|
|
44
|
+
|
|
45
|
+
if (userDataString) {
|
|
46
|
+
const userData = JSON.parse(userDataString) as SessionUser
|
|
47
|
+
const isExpired = userData.expires_at && userData.expires_at * 1000 < Date.now()
|
|
48
|
+
|
|
49
|
+
if (!isExpired) {
|
|
50
|
+
setState({
|
|
51
|
+
user: userData,
|
|
52
|
+
isLoading: false,
|
|
53
|
+
isAuthenticated: true,
|
|
54
|
+
accessToken: userData.access_token,
|
|
55
|
+
})
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
} catch (error) {
|
|
60
|
+
console.error('Error reading session:', error)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
setState({
|
|
64
|
+
user: null,
|
|
65
|
+
isLoading: false,
|
|
66
|
+
isAuthenticated: false,
|
|
67
|
+
accessToken: null,
|
|
68
|
+
})
|
|
69
|
+
}, [])
|
|
70
|
+
|
|
71
|
+
return state
|
|
72
|
+
}
|
package/index.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
// Auth exports
|
|
1
|
+
// Auth exports - Main app
|
|
2
2
|
export { ZitadelProvider, useZitadel } from './ZitadelProvider'
|
|
3
3
|
export { ProtectedRoute } from './ProtectedRoute'
|
|
4
4
|
export { ZitadelAuthService } from './services/zitadel.auth.service'
|
|
5
5
|
export { createAuthConfig } from './config/zitadel.config'
|
|
6
6
|
export type { ZitadelConfig } from './config/zitadel.config'
|
|
7
|
+
|
|
8
|
+
// Auth exports - Micro-apps
|
|
9
|
+
export { SessionProvider, useSession } from './SessionProvider'
|
|
10
|
+
|
|
7
11
|
export * from './utils'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orsetra/shared-auth",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Shared authentication utilities for Orsetra platform using Zitadel",
|
|
5
5
|
"main": "./index.ts",
|
|
6
6
|
"types": "./index.ts",
|
|
@@ -14,10 +14,12 @@
|
|
|
14
14
|
"files": [
|
|
15
15
|
"index.ts",
|
|
16
16
|
"ZitadelProvider.tsx",
|
|
17
|
+
"SessionProvider.tsx",
|
|
17
18
|
"ProtectedRoute.tsx",
|
|
18
19
|
"config",
|
|
19
20
|
"services",
|
|
20
21
|
"components",
|
|
22
|
+
"hooks",
|
|
21
23
|
"utils",
|
|
22
24
|
"README.md"
|
|
23
25
|
],
|
|
@@ -51,6 +51,7 @@ export class ZitadelAuthService {
|
|
|
51
51
|
|
|
52
52
|
static async signOut() {
|
|
53
53
|
try {
|
|
54
|
+
document.cookie = 'auth_session=; path=/; max-age=0';
|
|
54
55
|
await this.getAuth().signout();
|
|
55
56
|
} catch (error) {
|
|
56
57
|
throw this.handleAuthError(error);
|
|
@@ -68,7 +69,16 @@ export class ZitadelAuthService {
|
|
|
68
69
|
|
|
69
70
|
static async handleCallback(): Promise<User> {
|
|
70
71
|
try {
|
|
71
|
-
|
|
72
|
+
const user = await this.getAuth().userManager.signinRedirectCallback();
|
|
73
|
+
console.log(user)
|
|
74
|
+
if (user?.access_token) {
|
|
75
|
+
const maxAge = user.expires_at
|
|
76
|
+
? user.expires_at - Math.floor(Date.now() / 1000)
|
|
77
|
+
: 3600;
|
|
78
|
+
document.cookie = `auth_session=1; path=/; max-age=${maxAge}; SameSite=Lax`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return user;
|
|
72
82
|
} catch (error) {
|
|
73
83
|
throw this.handleAuthError(error);
|
|
74
84
|
}
|