@confighub/react-auth 0.1.3 → 0.2.0

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/dist/index.d.cts CHANGED
@@ -2,19 +2,97 @@ import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
3
  import { ConfigHubClient } from '@confighub/api';
4
4
 
5
+ interface Discovery {
6
+ AuthIssuer?: string;
7
+ TokenExchangeEndpoint?: string;
8
+ TokenExchangeAudience?: string;
9
+ }
10
+ interface MintedSession {
11
+ accessToken: string;
12
+ organizationId: string;
13
+ /** Claims of the validated IdP token (owning-org, audience, organization shape). */
14
+ idpClaims: Record<string, unknown>;
15
+ /**
16
+ * The IdP's ID token, kept only so logout can pass it as `id_token_hint` to the
17
+ * end-session endpoint. Absent for sessions that did not come from an OIDC login.
18
+ */
19
+ idToken?: string;
20
+ }
21
+ interface LoginOptions {
22
+ /**
23
+ * Where to land after login, as a same-origin path (`/space/x?tab=units`). Carried
24
+ * through the authorize round trip in the PKCE state, never in the redirect URI:
25
+ * OAuth clients register exact redirect URIs, so the URI itself must not vary with
26
+ * the page the user started from. Defaults to the current path and query.
27
+ */
28
+ returnTo?: string;
29
+ /**
30
+ * Keycloak organization alias to sign in to, sent as the `organization:<alias>`
31
+ * scope. Without it Keycloak prompts a multi-org user to pick one (or uses the
32
+ * organization already selected in the SSO session).
33
+ */
34
+ organization?: string;
35
+ /**
36
+ * `'none'` asks the IdP to re-authenticate without any UI, failing with
37
+ * `login_required` if the SSO session is gone -- the way to refresh an expired
38
+ * ConfigHub token when the user is still signed in at the IdP. `'login'` forces the
39
+ * login form even with a live SSO session.
40
+ */
41
+ prompt?: 'none' | 'login';
42
+ }
43
+ interface FlowOptions {
44
+ /**
45
+ * Same-origin path the IdP redirects back to, and therefore the redirect URI to
46
+ * register for the client: `{origin}{callbackPath}`. Defaults to `/`.
47
+ */
48
+ callbackPath?: string;
49
+ }
50
+ /** The fixed callback URI: the page origin plus the configured callback path. */
51
+ declare const callbackUri: (opts?: FlowOptions) => string;
52
+ /** Decode a JWT's claims without verifying it. Returns {} for anything malformed. */
53
+ declare function decodeJwtClaims(token: string): Record<string, unknown>;
54
+ /** Whether a JWT's `exp` is in the past (with a small skew allowance). */
55
+ declare function isExpired(token: string, skewSeconds?: number): boolean;
56
+
5
57
  type AuthStatus = 'loading' | 'authenticated' | 'unauthenticated' | 'error';
6
58
  interface ConfigHubUser {
7
59
  organizationId: string;
8
60
  idpClaims: Record<string, unknown>;
9
61
  }
62
+ interface LogoutOptions {
63
+ /**
64
+ * Also end the IdP session (RP-initiated logout), so the next login asks for
65
+ * credentials instead of riding the SSO cookie. Redirects the page; the landing
66
+ * URI must be registered for the client. Default: false, which only forgets the
67
+ * token in this tab.
68
+ */
69
+ endSession?: boolean;
70
+ /** Where to land after IdP logout. Defaults to the callback URI. */
71
+ postLogoutRedirectUri?: string;
72
+ }
10
73
  interface ConfigHubAuthContextValue {
11
74
  status: AuthStatus;
12
75
  user: ConfigHubUser | null;
13
76
  error: Error | null;
14
77
  /** Begin login: redirects the page to the IdP. */
15
- login: () => Promise<void>;
16
- /** Clear the in-memory session. Does not call the IdP end-session endpoint. */
17
- logout: () => void;
78
+ login: (options?: LoginOptions) => Promise<void>;
79
+ /** Forget the session in this tab and, optionally, end the IdP session too. */
80
+ logout: (options?: LogoutOptions) => Promise<void>;
81
+ /**
82
+ * Re-mint the ConfigHub token for another organization the user belongs to. The
83
+ * IdP session is untouched. Rejects with the server's error if the user is not a
84
+ * member; the current session stays as it was.
85
+ */
86
+ switchOrganization: (organizationId: string) => Promise<void>;
87
+ /**
88
+ * The ConfigHub token stopped working (a 401). Try to get a new one without any
89
+ * UI: a `prompt=none` round trip through the IdP, for the organization the session
90
+ * already had. Status goes to `loading` meanwhile, not `unauthenticated`, so an
91
+ * app that auto-logs-in on `unauthenticated` does not race this with an
92
+ * interactive login. If the IdP session is gone too, the page comes back
93
+ * `unauthenticated`. Redirects the page.
94
+ */
95
+ reauthenticate: () => Promise<void>;
18
96
  /** Current bearer token, or undefined when unauthenticated. */
19
97
  getToken: () => string | undefined;
20
98
  /** A typed API client pre-wired with the current token. Stable across renders. */
@@ -26,14 +104,33 @@ interface ConfigHubAuthProviderProps {
26
104
  baseUrl: string;
27
105
  /** This app's registered OAuth `client_id` (from `cub oauthclient create`). */
28
106
  clientId: string;
107
+ /**
108
+ * Same-origin path the IdP redirects back to; `{origin}{callbackPath}` is the
109
+ * redirect URI to register. Defaults to `/`. Fixed on purpose: the page a user
110
+ * starts login from travels in the PKCE state, not in the redirect URI.
111
+ */
112
+ callbackPath?: string;
113
+ /**
114
+ * `'session'` keeps the minted token in `sessionStorage` so a reload or an in-tab
115
+ * navigation does not round-trip through the IdP. Tab-scoped and gone when the tab
116
+ * closes. Default `'none'`: memory only, a reload starts unauthenticated.
117
+ */
118
+ persist?: 'none' | 'session';
119
+ /**
120
+ * What a 401 from the API means. `'login'` (default): the token is stale, try a
121
+ * silent re-authentication (`prompt=none`) and fall back to unauthenticated if the
122
+ * IdP session is gone too. `'logout'`: just drop the session.
123
+ */
124
+ onUnauthorized?: 'login' | 'logout';
29
125
  children: ReactNode;
30
126
  }
31
127
  /**
32
128
  * Runs the browser-direct auth flow and manages the token lifecycle. On mount it
33
- * completes a redirect if the page is the IdP callback; otherwise it starts
34
- * unauthenticated until `login()` is called.
129
+ * completes a redirect if the page is the IdP callback, restores a persisted
130
+ * session if there is one, and otherwise starts unauthenticated until `login()`
131
+ * is called.
35
132
  */
36
- declare function ConfigHubAuthProvider({ baseUrl, clientId, children, }: ConfigHubAuthProviderProps): JSX.Element;
133
+ declare function ConfigHubAuthProvider({ baseUrl, clientId, callbackPath, persist, onUnauthorized, children, }: ConfigHubAuthProviderProps): JSX.Element;
37
134
 
38
135
  /**
39
136
  * Access the ConfigHub auth state and actions. Must be called under a
@@ -63,16 +160,4 @@ declare function useConfigHub(): ConfigHubClient;
63
160
  */
64
161
  declare function getAccessToken(): string | undefined;
65
162
 
66
- interface Discovery {
67
- AuthIssuer?: string;
68
- TokenExchangeEndpoint?: string;
69
- TokenExchangeAudience?: string;
70
- }
71
- interface MintedSession {
72
- accessToken: string;
73
- organizationId: string;
74
- /** Claims of the validated IdP token (owning-org, audience, organization shape). */
75
- idpClaims: Record<string, unknown>;
76
- }
77
-
78
- export { type AuthStatus, ConfigHubAuthContext, type ConfigHubAuthContextValue, ConfigHubAuthProvider, type ConfigHubAuthProviderProps, type ConfigHubUser, type Discovery, type MintedSession, getAccessToken, useAuth, useConfigHub };
163
+ export { type AuthStatus, ConfigHubAuthContext, type ConfigHubAuthContextValue, ConfigHubAuthProvider, type ConfigHubAuthProviderProps, type ConfigHubUser, type Discovery, type FlowOptions, type LoginOptions, type LogoutOptions, type MintedSession, callbackUri, decodeJwtClaims, getAccessToken, isExpired, useAuth, useConfigHub };
package/dist/index.d.ts CHANGED
@@ -2,19 +2,97 @@ import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
3
  import { ConfigHubClient } from '@confighub/api';
4
4
 
5
+ interface Discovery {
6
+ AuthIssuer?: string;
7
+ TokenExchangeEndpoint?: string;
8
+ TokenExchangeAudience?: string;
9
+ }
10
+ interface MintedSession {
11
+ accessToken: string;
12
+ organizationId: string;
13
+ /** Claims of the validated IdP token (owning-org, audience, organization shape). */
14
+ idpClaims: Record<string, unknown>;
15
+ /**
16
+ * The IdP's ID token, kept only so logout can pass it as `id_token_hint` to the
17
+ * end-session endpoint. Absent for sessions that did not come from an OIDC login.
18
+ */
19
+ idToken?: string;
20
+ }
21
+ interface LoginOptions {
22
+ /**
23
+ * Where to land after login, as a same-origin path (`/space/x?tab=units`). Carried
24
+ * through the authorize round trip in the PKCE state, never in the redirect URI:
25
+ * OAuth clients register exact redirect URIs, so the URI itself must not vary with
26
+ * the page the user started from. Defaults to the current path and query.
27
+ */
28
+ returnTo?: string;
29
+ /**
30
+ * Keycloak organization alias to sign in to, sent as the `organization:<alias>`
31
+ * scope. Without it Keycloak prompts a multi-org user to pick one (or uses the
32
+ * organization already selected in the SSO session).
33
+ */
34
+ organization?: string;
35
+ /**
36
+ * `'none'` asks the IdP to re-authenticate without any UI, failing with
37
+ * `login_required` if the SSO session is gone -- the way to refresh an expired
38
+ * ConfigHub token when the user is still signed in at the IdP. `'login'` forces the
39
+ * login form even with a live SSO session.
40
+ */
41
+ prompt?: 'none' | 'login';
42
+ }
43
+ interface FlowOptions {
44
+ /**
45
+ * Same-origin path the IdP redirects back to, and therefore the redirect URI to
46
+ * register for the client: `{origin}{callbackPath}`. Defaults to `/`.
47
+ */
48
+ callbackPath?: string;
49
+ }
50
+ /** The fixed callback URI: the page origin plus the configured callback path. */
51
+ declare const callbackUri: (opts?: FlowOptions) => string;
52
+ /** Decode a JWT's claims without verifying it. Returns {} for anything malformed. */
53
+ declare function decodeJwtClaims(token: string): Record<string, unknown>;
54
+ /** Whether a JWT's `exp` is in the past (with a small skew allowance). */
55
+ declare function isExpired(token: string, skewSeconds?: number): boolean;
56
+
5
57
  type AuthStatus = 'loading' | 'authenticated' | 'unauthenticated' | 'error';
6
58
  interface ConfigHubUser {
7
59
  organizationId: string;
8
60
  idpClaims: Record<string, unknown>;
9
61
  }
62
+ interface LogoutOptions {
63
+ /**
64
+ * Also end the IdP session (RP-initiated logout), so the next login asks for
65
+ * credentials instead of riding the SSO cookie. Redirects the page; the landing
66
+ * URI must be registered for the client. Default: false, which only forgets the
67
+ * token in this tab.
68
+ */
69
+ endSession?: boolean;
70
+ /** Where to land after IdP logout. Defaults to the callback URI. */
71
+ postLogoutRedirectUri?: string;
72
+ }
10
73
  interface ConfigHubAuthContextValue {
11
74
  status: AuthStatus;
12
75
  user: ConfigHubUser | null;
13
76
  error: Error | null;
14
77
  /** Begin login: redirects the page to the IdP. */
15
- login: () => Promise<void>;
16
- /** Clear the in-memory session. Does not call the IdP end-session endpoint. */
17
- logout: () => void;
78
+ login: (options?: LoginOptions) => Promise<void>;
79
+ /** Forget the session in this tab and, optionally, end the IdP session too. */
80
+ logout: (options?: LogoutOptions) => Promise<void>;
81
+ /**
82
+ * Re-mint the ConfigHub token for another organization the user belongs to. The
83
+ * IdP session is untouched. Rejects with the server's error if the user is not a
84
+ * member; the current session stays as it was.
85
+ */
86
+ switchOrganization: (organizationId: string) => Promise<void>;
87
+ /**
88
+ * The ConfigHub token stopped working (a 401). Try to get a new one without any
89
+ * UI: a `prompt=none` round trip through the IdP, for the organization the session
90
+ * already had. Status goes to `loading` meanwhile, not `unauthenticated`, so an
91
+ * app that auto-logs-in on `unauthenticated` does not race this with an
92
+ * interactive login. If the IdP session is gone too, the page comes back
93
+ * `unauthenticated`. Redirects the page.
94
+ */
95
+ reauthenticate: () => Promise<void>;
18
96
  /** Current bearer token, or undefined when unauthenticated. */
19
97
  getToken: () => string | undefined;
20
98
  /** A typed API client pre-wired with the current token. Stable across renders. */
@@ -26,14 +104,33 @@ interface ConfigHubAuthProviderProps {
26
104
  baseUrl: string;
27
105
  /** This app's registered OAuth `client_id` (from `cub oauthclient create`). */
28
106
  clientId: string;
107
+ /**
108
+ * Same-origin path the IdP redirects back to; `{origin}{callbackPath}` is the
109
+ * redirect URI to register. Defaults to `/`. Fixed on purpose: the page a user
110
+ * starts login from travels in the PKCE state, not in the redirect URI.
111
+ */
112
+ callbackPath?: string;
113
+ /**
114
+ * `'session'` keeps the minted token in `sessionStorage` so a reload or an in-tab
115
+ * navigation does not round-trip through the IdP. Tab-scoped and gone when the tab
116
+ * closes. Default `'none'`: memory only, a reload starts unauthenticated.
117
+ */
118
+ persist?: 'none' | 'session';
119
+ /**
120
+ * What a 401 from the API means. `'login'` (default): the token is stale, try a
121
+ * silent re-authentication (`prompt=none`) and fall back to unauthenticated if the
122
+ * IdP session is gone too. `'logout'`: just drop the session.
123
+ */
124
+ onUnauthorized?: 'login' | 'logout';
29
125
  children: ReactNode;
30
126
  }
31
127
  /**
32
128
  * Runs the browser-direct auth flow and manages the token lifecycle. On mount it
33
- * completes a redirect if the page is the IdP callback; otherwise it starts
34
- * unauthenticated until `login()` is called.
129
+ * completes a redirect if the page is the IdP callback, restores a persisted
130
+ * session if there is one, and otherwise starts unauthenticated until `login()`
131
+ * is called.
35
132
  */
36
- declare function ConfigHubAuthProvider({ baseUrl, clientId, children, }: ConfigHubAuthProviderProps): JSX.Element;
133
+ declare function ConfigHubAuthProvider({ baseUrl, clientId, callbackPath, persist, onUnauthorized, children, }: ConfigHubAuthProviderProps): JSX.Element;
37
134
 
38
135
  /**
39
136
  * Access the ConfigHub auth state and actions. Must be called under a
@@ -63,16 +160,4 @@ declare function useConfigHub(): ConfigHubClient;
63
160
  */
64
161
  declare function getAccessToken(): string | undefined;
65
162
 
66
- interface Discovery {
67
- AuthIssuer?: string;
68
- TokenExchangeEndpoint?: string;
69
- TokenExchangeAudience?: string;
70
- }
71
- interface MintedSession {
72
- accessToken: string;
73
- organizationId: string;
74
- /** Claims of the validated IdP token (owning-org, audience, organization shape). */
75
- idpClaims: Record<string, unknown>;
76
- }
77
-
78
- export { type AuthStatus, ConfigHubAuthContext, type ConfigHubAuthContextValue, ConfigHubAuthProvider, type ConfigHubAuthProviderProps, type ConfigHubUser, type Discovery, type MintedSession, getAccessToken, useAuth, useConfigHub };
163
+ export { type AuthStatus, ConfigHubAuthContext, type ConfigHubAuthContextValue, ConfigHubAuthProvider, type ConfigHubAuthProviderProps, type ConfigHubUser, type Discovery, type FlowOptions, type LoginOptions, type LogoutOptions, type MintedSession, callbackUri, decodeJwtClaims, getAccessToken, isExpired, useAuth, useConfigHub };