@orsetra/shared-auth 1.0.1 → 1.0.2

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.
@@ -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.1",
3
+ "version": "1.0.2",
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,15 @@ export class ZitadelAuthService {
68
69
 
69
70
  static async handleCallback(): Promise<User> {
70
71
  try {
71
- return await this.getAuth().userManager.signinRedirectCallback();
72
+ const user = await this.getAuth().userManager.signinRedirectCallback();
73
+ if (user?.access_token) {
74
+ const maxAge = user.expires_at
75
+ ? user.expires_at - Math.floor(Date.now() / 1000)
76
+ : 3600;
77
+ document.cookie = `auth_session=1; path=/; max-age=${maxAge}; SameSite=Lax`;
78
+ }
79
+
80
+ return user;
72
81
  } catch (error) {
73
82
  throw this.handleAuthError(error);
74
83
  }