@authaction/web-sdk 0.1.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/README.md ADDED
@@ -0,0 +1,239 @@
1
+ # @authaction/web-sdk
2
+
3
+ Browser-side OAuth2 / PKCE SDK for AuthAction. Framework-agnostic core with first-class wrappers for **React**, **Next.js**, **Angular**, **Vue 3**, and **Vanilla JS**.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @authaction/web-sdk
9
+ ```
10
+
11
+ ## Frameworks
12
+
13
+ | Import path | Framework |
14
+ |---|---|
15
+ | `@authaction/web-sdk` | Vanilla JS / TypeScript |
16
+ | `@authaction/web-sdk/react` | React 17+ |
17
+ | `@authaction/web-sdk/nextjs` | Next.js 14+ (App Router, client-side) |
18
+ | `@authaction/web-sdk/angular` | Angular 17+ |
19
+ | `@authaction/web-sdk/vue` | Vue 3 |
20
+
21
+ ---
22
+
23
+ ## React
24
+
25
+ ```tsx
26
+ // main.tsx
27
+ import { AuthActionProvider } from '@authaction/web-sdk/react';
28
+
29
+ <AuthActionProvider
30
+ domain="myapp.eu.authaction.com"
31
+ clientId="your-client-id"
32
+ redirectUri={`${window.location.origin}/`}
33
+ postLogoutRedirectUri={`${window.location.origin}/`}
34
+ authorizationParams={{ audience: 'https://api.myapp.com' }}
35
+ >
36
+ <App />
37
+ </AuthActionProvider>
38
+ ```
39
+
40
+ ```tsx
41
+ // App.tsx
42
+ import { useAuthAction, hasAuthParams } from '@authaction/web-sdk/react';
43
+
44
+ function App() {
45
+ const { isLoading, isAuthenticated, user, loginWithRedirect, logout } = useAuthAction();
46
+
47
+ if (isLoading) return <Spinner />;
48
+ if (!isAuthenticated) return <button onClick={() => loginWithRedirect()}>Login</button>;
49
+ return (
50
+ <div>
51
+ <p>Hello {user?.name}</p>
52
+ <button onClick={() => logout()}>Logout</button>
53
+ </div>
54
+ );
55
+ }
56
+ ```
57
+
58
+ ### react-oidc-context compatibility
59
+
60
+ The SDK is a drop-in replacement for `react-oidc-context`. The following are provided for compatibility:
61
+
62
+ | react-oidc-context | @authaction/web-sdk/react |
63
+ |---|---|
64
+ | `useAuth()` | `useAuthAction()` |
65
+ | `auth.signinRedirect()` | `auth.loginWithRedirect()` |
66
+ | `auth.signoutRedirect()` | `auth.logout()` |
67
+ | `auth.removeUser()` | `auth.removeUser()` ✓ same |
68
+ | `auth.user?.access_token` | `auth.user?.access_token` ✓ same |
69
+ | `auth.user?.profile.*` | `auth.user?.profile.*` ✓ same |
70
+ | `auth.activeNavigator` | `auth.activeNavigator` ✓ same |
71
+ | `hasAuthParams()` | `hasAuthParams()` ✓ same |
72
+
73
+ ---
74
+
75
+ ## Next.js (App Router)
76
+
77
+ ```tsx
78
+ // app/layout.tsx
79
+ import { AuthActionNextProvider } from '@authaction/web-sdk/nextjs';
80
+
81
+ export default function RootLayout({ children }) {
82
+ return (
83
+ <html><body>
84
+ <AuthActionNextProvider
85
+ domain="myapp.eu.authaction.com"
86
+ clientId="your-client-id"
87
+ redirectUri="http://localhost:3000/"
88
+ >
89
+ {children}
90
+ </AuthActionNextProvider>
91
+ </body></html>
92
+ );
93
+ }
94
+ ```
95
+
96
+ ```tsx
97
+ // Any client component
98
+ 'use client';
99
+ import { useAuthAction } from '@authaction/web-sdk/nextjs';
100
+
101
+ export function Profile() {
102
+ const { user, logout } = useAuthAction();
103
+ return <button onClick={() => logout()}>{user?.name}</button>;
104
+ }
105
+ ```
106
+
107
+ > For server-side sessions (NextAuth.js pattern) use [`@authaction/server-sdk/nextjs`](https://github.com/authaction/authaction-server-sdk) instead.
108
+
109
+ ---
110
+
111
+ ## Angular
112
+
113
+ ```ts
114
+ // main.ts
115
+ import { provideAuthAction } from '@authaction/web-sdk/angular';
116
+
117
+ bootstrapApplication(AppComponent, {
118
+ providers: [
119
+ provideAuthAction({
120
+ domain: 'myapp.eu.authaction.com',
121
+ clientId: 'your-client-id',
122
+ redirectUri: 'http://localhost:4200/',
123
+ }),
124
+ ],
125
+ });
126
+ ```
127
+
128
+ ```ts
129
+ // app.component.ts
130
+ import { AuthActionService } from '@authaction/web-sdk/angular';
131
+
132
+ @Component({ ... })
133
+ export class AppComponent {
134
+ constructor(public auth: AuthActionService) {}
135
+
136
+ login() { this.auth.loginWithRedirect(); }
137
+ logout() { this.auth.logout(); }
138
+ }
139
+ ```
140
+
141
+ ```html
142
+ <ng-container *ngIf="auth.isAuthenticated$ | async">
143
+ Welcome {{ (auth.user$ | async)?.name }}
144
+ </ng-container>
145
+ ```
146
+
147
+ ### Route guard
148
+
149
+ ```ts
150
+ // app.routes.ts
151
+ { path: 'dashboard', canActivate: [authActionGuard], component: DashboardComponent }
152
+ ```
153
+
154
+ ---
155
+
156
+ ## Vue 3
157
+
158
+ ```ts
159
+ // main.ts
160
+ import { createApp } from 'vue';
161
+ import { createAuthAction } from '@authaction/web-sdk/vue';
162
+
163
+ const app = createApp(App);
164
+ app.use(createAuthAction({
165
+ domain: 'myapp.eu.authaction.com',
166
+ clientId: 'your-client-id',
167
+ redirectUri: 'http://localhost:5173/callback',
168
+ }));
169
+ app.mount('#app');
170
+ ```
171
+
172
+ ```vue
173
+ <script setup lang="ts">
174
+ import { useAuthAction } from '@authaction/web-sdk/vue';
175
+
176
+ const { state, loginWithRedirect, logout } = useAuthAction();
177
+ </script>
178
+
179
+ <template>
180
+ <div v-if="state.isAuthenticated">
181
+ Hello {{ state.user?.name }}
182
+ <button @click="logout()">Logout</button>
183
+ </div>
184
+ <button v-else @click="loginWithRedirect()">Login</button>
185
+ </template>
186
+ ```
187
+
188
+ ---
189
+
190
+ ## Vanilla JS / TypeScript
191
+
192
+ ```ts
193
+ import { AuthActionClient } from '@authaction/web-sdk';
194
+
195
+ const client = new AuthActionClient({
196
+ domain: 'myapp.eu.authaction.com',
197
+ clientId: 'your-client-id',
198
+ redirectUri: 'http://localhost:5173/callback',
199
+ });
200
+
201
+ // Login
202
+ await client.loginWithRedirect();
203
+
204
+ // Handle callback (call on your redirect URI page)
205
+ await client.handleRedirectCallback();
206
+
207
+ // Get access token (auto-refreshes if expired)
208
+ const token = await client.getAccessToken();
209
+
210
+ // Logout
211
+ await client.logout({ returnTo: 'http://localhost:5173' });
212
+ ```
213
+
214
+ ---
215
+
216
+ ## Configuration
217
+
218
+ | Option | Type | Required | Description |
219
+ |---|---|---|---|
220
+ | `domain` | `string` | ✓ | AuthAction tenant domain |
221
+ | `clientId` | `string` | ✓ | OAuth2 client ID |
222
+ | `redirectUri` | `string` | ✓ | Callback URL after login |
223
+ | `postLogoutRedirectUri` | `string` | | Redirect URL after logout |
224
+ | `scope` | `string` | | OAuth2 scopes (default: `openid profile email`) |
225
+ | `authorizationParams` | `object` | | Extra params forwarded to `/authorize` (e.g. `audience`) |
226
+ | `cacheLocation` | `memory` \| `localstorage` \| `sessionstorage` | | Token storage (default: `memory`) |
227
+
228
+ ## Environment variables
229
+
230
+ ```bash
231
+ AUTHACTION_DOMAIN=your-tenant.eu.authaction.com
232
+ AUTHACTION_CLIENT_ID=your-client-id
233
+ AUTHACTION_REDIRECT_URI=http://localhost:3000/
234
+ AUTHACTION_AUDIENCE=https://api.your-app.com
235
+ ```
236
+
237
+ ## License
238
+
239
+ MIT
@@ -0,0 +1,116 @@
1
+ import { OnDestroy, ModuleWithProviders, EnvironmentProviders, InjectionToken } from '@angular/core';
2
+ import { Observable } from 'rxjs';
3
+ import { d as AuthState, U as User, A as AuthActionClientConfig, L as LoginOptions, a as LoginWithPopupOptions, R as RedirectCallbackResult, G as GetAccessTokenOptions, b as LogoutOptions } from '../utils-BzTlGuFj.mjs';
4
+ export { h as hasAuthParams } from '../utils-BzTlGuFj.mjs';
5
+ import { CanActivateFn } from '@angular/router';
6
+
7
+ declare class AuthActionService implements OnDestroy {
8
+ private readonly client;
9
+ private readonly _state$;
10
+ private readonly _unsub;
11
+ /** Full auth state stream */
12
+ readonly state$: Observable<AuthState>;
13
+ /** Emits true/false as authentication state changes */
14
+ readonly isAuthenticated$: Observable<boolean>;
15
+ /** Emits the decoded user profile, or undefined when not authenticated */
16
+ readonly user$: Observable<User | undefined>;
17
+ /** Emits true while the SDK is resolving the initial auth state */
18
+ readonly isLoading$: Observable<boolean>;
19
+ /** Emits the current error, or undefined when there is none */
20
+ readonly error$: Observable<Error | undefined>;
21
+ /** Emits which navigation is in-flight ('loginWithRedirect' | 'logout'), or undefined when idle */
22
+ readonly activeNavigator$: Observable<'loginWithRedirect' | 'logout' | undefined>;
23
+ constructor(config: AuthActionClientConfig);
24
+ /** Redirect to AuthAction login page using PKCE authorization code flow */
25
+ loginWithRedirect(options?: LoginOptions): Promise<void>;
26
+ /** Open AuthAction login in a popup window */
27
+ loginWithPopup(options?: LoginWithPopupOptions): Promise<void>;
28
+ /**
29
+ * Handle the OAuth2 redirect callback.
30
+ * Call this in the component or resolver mounted at your redirectUri route.
31
+ */
32
+ handleRedirectCallback(url?: string): Promise<RedirectCallbackResult>;
33
+ /**
34
+ * Get a valid access token, auto-refreshing if expired.
35
+ * Use as the Bearer token for API calls:
36
+ *
37
+ * ```ts
38
+ * const token = await this.auth.getAccessToken();
39
+ * this.http.get('/api/files', {
40
+ * headers: { Authorization: `Bearer ${token}` },
41
+ * });
42
+ * ```
43
+ */
44
+ getAccessToken(options?: GetAccessTokenOptions): Promise<string>;
45
+ /** Log out and optionally end the AuthAction SSO session */
46
+ logout(options?: LogoutOptions): Promise<void>;
47
+ /**
48
+ * Clear the local session without hitting the server's end-session endpoint.
49
+ * Use this on 401 API responses where the server already invalidated the session.
50
+ */
51
+ removeUser(): void;
52
+ /** Synchronous snapshot — use observables for reactive updates */
53
+ get isAuthenticated(): boolean;
54
+ /** Synchronous snapshot — use user$ for reactive updates */
55
+ get user(): User | undefined;
56
+ ngOnDestroy(): void;
57
+ }
58
+
59
+ /**
60
+ * Provides AuthAction for standalone applications (Angular 14+).
61
+ *
62
+ * @example
63
+ * ```ts
64
+ * // main.ts
65
+ * bootstrapApplication(AppComponent, {
66
+ * providers: [
67
+ * provideAuthAction({
68
+ * domain: 'myapp.eu.authaction.com',
69
+ * clientId: 'your-client-id',
70
+ * redirectUri: 'http://localhost:4200/callback',
71
+ * }),
72
+ * ],
73
+ * });
74
+ * ```
75
+ */
76
+ declare function provideAuthAction(config: AuthActionClientConfig): EnvironmentProviders;
77
+ /**
78
+ * Angular module for module-based applications.
79
+ *
80
+ * @example
81
+ * ```ts
82
+ * // app.module.ts
83
+ * @NgModule({
84
+ * imports: [
85
+ * AuthActionModule.forRoot({
86
+ * domain: 'myapp.eu.authaction.com',
87
+ * clientId: 'your-client-id',
88
+ * redirectUri: 'http://localhost:4200/callback',
89
+ * }),
90
+ * ],
91
+ * })
92
+ * export class AppModule {}
93
+ * ```
94
+ */
95
+ declare class AuthActionModule {
96
+ static forRoot(config: AuthActionClientConfig): ModuleWithProviders<AuthActionModule>;
97
+ }
98
+
99
+ /**
100
+ * Route guard that redirects unauthenticated users to login.
101
+ *
102
+ * @example
103
+ * ```ts
104
+ * // app.routes.ts
105
+ * {
106
+ * path: 'dashboard',
107
+ * canActivate: [authActionGuard],
108
+ * component: DashboardComponent,
109
+ * }
110
+ * ```
111
+ */
112
+ declare const authActionGuard: CanActivateFn;
113
+
114
+ declare const AUTHACTION_CONFIG: InjectionToken<AuthActionClientConfig>;
115
+
116
+ export { AUTHACTION_CONFIG, AuthActionModule, AuthActionService, authActionGuard, provideAuthAction };
@@ -0,0 +1,116 @@
1
+ import { OnDestroy, ModuleWithProviders, EnvironmentProviders, InjectionToken } from '@angular/core';
2
+ import { Observable } from 'rxjs';
3
+ import { d as AuthState, U as User, A as AuthActionClientConfig, L as LoginOptions, a as LoginWithPopupOptions, R as RedirectCallbackResult, G as GetAccessTokenOptions, b as LogoutOptions } from '../utils-BzTlGuFj.js';
4
+ export { h as hasAuthParams } from '../utils-BzTlGuFj.js';
5
+ import { CanActivateFn } from '@angular/router';
6
+
7
+ declare class AuthActionService implements OnDestroy {
8
+ private readonly client;
9
+ private readonly _state$;
10
+ private readonly _unsub;
11
+ /** Full auth state stream */
12
+ readonly state$: Observable<AuthState>;
13
+ /** Emits true/false as authentication state changes */
14
+ readonly isAuthenticated$: Observable<boolean>;
15
+ /** Emits the decoded user profile, or undefined when not authenticated */
16
+ readonly user$: Observable<User | undefined>;
17
+ /** Emits true while the SDK is resolving the initial auth state */
18
+ readonly isLoading$: Observable<boolean>;
19
+ /** Emits the current error, or undefined when there is none */
20
+ readonly error$: Observable<Error | undefined>;
21
+ /** Emits which navigation is in-flight ('loginWithRedirect' | 'logout'), or undefined when idle */
22
+ readonly activeNavigator$: Observable<'loginWithRedirect' | 'logout' | undefined>;
23
+ constructor(config: AuthActionClientConfig);
24
+ /** Redirect to AuthAction login page using PKCE authorization code flow */
25
+ loginWithRedirect(options?: LoginOptions): Promise<void>;
26
+ /** Open AuthAction login in a popup window */
27
+ loginWithPopup(options?: LoginWithPopupOptions): Promise<void>;
28
+ /**
29
+ * Handle the OAuth2 redirect callback.
30
+ * Call this in the component or resolver mounted at your redirectUri route.
31
+ */
32
+ handleRedirectCallback(url?: string): Promise<RedirectCallbackResult>;
33
+ /**
34
+ * Get a valid access token, auto-refreshing if expired.
35
+ * Use as the Bearer token for API calls:
36
+ *
37
+ * ```ts
38
+ * const token = await this.auth.getAccessToken();
39
+ * this.http.get('/api/files', {
40
+ * headers: { Authorization: `Bearer ${token}` },
41
+ * });
42
+ * ```
43
+ */
44
+ getAccessToken(options?: GetAccessTokenOptions): Promise<string>;
45
+ /** Log out and optionally end the AuthAction SSO session */
46
+ logout(options?: LogoutOptions): Promise<void>;
47
+ /**
48
+ * Clear the local session without hitting the server's end-session endpoint.
49
+ * Use this on 401 API responses where the server already invalidated the session.
50
+ */
51
+ removeUser(): void;
52
+ /** Synchronous snapshot — use observables for reactive updates */
53
+ get isAuthenticated(): boolean;
54
+ /** Synchronous snapshot — use user$ for reactive updates */
55
+ get user(): User | undefined;
56
+ ngOnDestroy(): void;
57
+ }
58
+
59
+ /**
60
+ * Provides AuthAction for standalone applications (Angular 14+).
61
+ *
62
+ * @example
63
+ * ```ts
64
+ * // main.ts
65
+ * bootstrapApplication(AppComponent, {
66
+ * providers: [
67
+ * provideAuthAction({
68
+ * domain: 'myapp.eu.authaction.com',
69
+ * clientId: 'your-client-id',
70
+ * redirectUri: 'http://localhost:4200/callback',
71
+ * }),
72
+ * ],
73
+ * });
74
+ * ```
75
+ */
76
+ declare function provideAuthAction(config: AuthActionClientConfig): EnvironmentProviders;
77
+ /**
78
+ * Angular module for module-based applications.
79
+ *
80
+ * @example
81
+ * ```ts
82
+ * // app.module.ts
83
+ * @NgModule({
84
+ * imports: [
85
+ * AuthActionModule.forRoot({
86
+ * domain: 'myapp.eu.authaction.com',
87
+ * clientId: 'your-client-id',
88
+ * redirectUri: 'http://localhost:4200/callback',
89
+ * }),
90
+ * ],
91
+ * })
92
+ * export class AppModule {}
93
+ * ```
94
+ */
95
+ declare class AuthActionModule {
96
+ static forRoot(config: AuthActionClientConfig): ModuleWithProviders<AuthActionModule>;
97
+ }
98
+
99
+ /**
100
+ * Route guard that redirects unauthenticated users to login.
101
+ *
102
+ * @example
103
+ * ```ts
104
+ * // app.routes.ts
105
+ * {
106
+ * path: 'dashboard',
107
+ * canActivate: [authActionGuard],
108
+ * component: DashboardComponent,
109
+ * }
110
+ * ```
111
+ */
112
+ declare const authActionGuard: CanActivateFn;
113
+
114
+ declare const AUTHACTION_CONFIG: InjectionToken<AuthActionClientConfig>;
115
+
116
+ export { AUTHACTION_CONFIG, AuthActionModule, AuthActionService, authActionGuard, provideAuthAction };
@@ -0,0 +1,2 @@
1
+ "use strict";var g=Object.defineProperty;var R=Object.getOwnPropertyDescriptor;var J=Object.getOwnPropertyNames;var z=Object.prototype.hasOwnProperty;var D=(r,e)=>{for(var t in e)g(r,t,{get:e[t],enumerable:!0})},V=(r,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of J(e))!z.call(r,n)&&n!==t&&g(r,n,{get:()=>e[n],enumerable:!(i=R(e,n))||i.enumerable});return r};var B=r=>V(g({},"__esModule",{value:!0}),r),m=(r,e,t,i)=>{for(var n=i>1?void 0:i?R(e,t):e,o=r.length-1,s;o>=0;o--)(s=r[o])&&(n=(i?s(e,t,n):s(n))||n);return i&&n&&g(e,t,n),n},O=(r,e)=>(t,i)=>e(t,i,r);var K={};D(K,{AUTHACTION_CONFIG:()=>h,AuthActionModule:()=>d,AuthActionService:()=>c,authActionGuard:()=>j,hasAuthParams:()=>G,provideAuthAction:()=>F});module.exports=B(K);var v=require("@angular/core"),$=require("rxjs"),a=require("rxjs/operators");function u(r=64){let e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~",t=new Uint8Array(r);return crypto.getRandomValues(t),Array.from(t,i=>e[i%e.length]).join("")}function H(r){return btoa(String.fromCharCode(...new Uint8Array(r))).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}async function _(r){let e=new TextEncoder().encode(r),t=await crypto.subtle.digest("SHA-256",e);return H(t)}function y(r){let e=r.split(".");if(e.length!==3)throw new Error("Invalid JWT format");let t=e[1].replace(/-/g,"+").replace(/_/g,"/");try{return JSON.parse(atob(t))}catch{throw new Error("Failed to decode JWT payload")}}var C="__authaction_pkce__",w=()=>typeof sessionStorage<"u"?sessionStorage:null,p={save(r){w()?.setItem(C,JSON.stringify(r))},get(){let r=w()?.getItem(C)??null;return r?JSON.parse(r):null},clear(){w()?.removeItem(C)}},b="__authaction_tokens__",S=class{constructor(e="memory"){this.memStore=null;this.backend=e}get(){if(this.backend==="memory")return this.memStore;let e=this.getStore()?.getItem(b)??null;return e?JSON.parse(e):null}set(e){if(this.backend==="memory"){this.memStore=e;return}this.getStore()?.setItem(b,JSON.stringify(e))}clear(){this.memStore=null,this.backend!=="memory"&&this.getStore()?.removeItem(b)}getStore(){return this.backend==="localstorage"?typeof localStorage<"u"?localStorage:null:w()}};var x="openid profile email",M=60,k=class{constructor(e){this.listeners=new Set;this.state={isAuthenticated:!1,isLoading:!0,user:void 0,error:void 0};this.cfg=e,this.cache=new S(e.cacheLocation??"memory"),this.loadStateFromCache()}async loginWithRedirect(e={}){this.setState({...this.state,activeNavigator:"loginWithRedirect"});try{let t=u(32),i=u(64),n=await _(i);p.save({state:t,codeVerifier:i,redirectUri:this.cfg.redirectUri,appState:e.appState});let o=new URLSearchParams({response_type:"code",client_id:this.cfg.clientId,redirect_uri:this.cfg.redirectUri,scope:this.cfg.scope??x,state:t,code_challenge:n,code_challenge_method:"S256",...this.cfg.authorizationParams,...e.authorizationParams});this.navigate(`https://${this.cfg.domain}/oauth2/authorize?${o}`)}catch(t){throw this.setState({isAuthenticated:this.state.isAuthenticated,isLoading:!1,user:this.state.user,error:t instanceof Error?t:new Error(String(t))}),t}}async loginWithPopup(e={}){let t=u(32),i=u(64),n=await _(i);p.save({state:t,codeVerifier:i,redirectUri:this.cfg.redirectUri,appState:e.appState});let o=new URLSearchParams({response_type:"code",client_id:this.cfg.clientId,redirect_uri:this.cfg.redirectUri,scope:this.cfg.scope??x,state:t,code_challenge:n,code_challenge_method:"S256",...this.cfg.authorizationParams,...e.authorizationParams});if(!(e.popup??window.open(`https://${this.cfg.domain}/oauth2/authorize?${o}`,"authaction:login","width=480,height=640,left=100,top=100")))throw new Error("Popup was blocked. Allow popups for this site and try again.");await new Promise((f,T)=>{let L=async l=>{if(l.origin===window.location.origin&&l.data?.type?.startsWith("authaction:"))if(window.removeEventListener("message",L),l.data.type==="authaction:callback")try{let A=new URL(l.data.url);await this.exchangeCodeFromUrl(A),f()}catch(A){T(A)}else l.data.type==="authaction:error"&&T(new Error(l.data.error))};window.addEventListener("message",L)})}async handleRedirectCallback(e){let t=new URL(e??window.location.href),i=await this.exchangeCodeFromUrl(t),n=new URL(window.location.href);return n.searchParams.delete("code"),n.searchParams.delete("state"),window.history.replaceState({},document.title,n.toString()),{appState:i}}static handlePopupCallback(e){window.opener&&(window.opener.postMessage({type:"authaction:callback",url:e??window.location.href},window.location.origin),window.close())}async isAuthenticated(){let e=this.cache.get();if(!e)return!1;if(this.isExpired(e))try{await this.refreshTokens()}catch{return!1}return!0}async getUser(){if(!await this.isAuthenticated())return;let e=this.cache.get();if(e?.id_token)try{return y(e.id_token)}catch{return}}async getAccessToken(e={}){let t=this.cache.get();if(!t)throw new Error("Not authenticated \u2014 call loginWithRedirect() first");return(e.forceRefresh||this.isExpired(t))&&(t=await this.refreshTokens()),t.access_token}async logout(e={}){if(this.cache.clear(),e.federated===!1){this.setState({isAuthenticated:!1,isLoading:!1,user:void 0,error:void 0});return}this.setState({isAuthenticated:!1,isLoading:!1,user:void 0,error:void 0,activeNavigator:"logout"});let t=e.returnTo??this.cfg.postLogoutRedirectUri,i=new URLSearchParams({client_id:e.clientId??this.cfg.clientId,...t?{post_logout_redirect_uri:t}:{}});this.navigate(`https://${this.cfg.domain}/oidc/logout?${i}`)}removeUser(){this.cache.clear(),this.setState({isAuthenticated:!1,isLoading:!1,user:void 0,error:void 0})}onStateChange(e){return this.listeners.add(e),e(this.state),()=>this.listeners.delete(e)}getState(){return{...this.state}}async exchangeCodeFromUrl(e){let t=e.searchParams.get("code"),i=e.searchParams.get("state"),n=e.searchParams.get("error"),o=e.searchParams.get("error_description");if(n)throw new Error(o??n);if(!t)throw new Error("No authorization code in callback URL");let s=p.get();if(p.clear(),!s)throw new Error("No PKCE transaction found \u2014 did the login session expire?");if(s.state!==i)throw new Error("State mismatch \u2014 possible CSRF attack");let f=await this.exchangeCode(t,s.codeVerifier,s.redirectUri);return this.cache.set(f),this.setState({isAuthenticated:!0,isLoading:!1,user:this.buildUser(f),error:void 0}),s.appState}async exchangeCode(e,t,i){let n=await fetch(`https://${this.cfg.domain}/oauth2/token`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"authorization_code",client_id:this.cfg.clientId,code:e,redirect_uri:i,code_verifier:t})});if(!n.ok){let s=await n.json().catch(()=>({}));throw new Error(s.error_description??s.error??"Token exchange failed")}let o=await n.json();return this.normaliseTokenResponse(o)}async refreshTokens(){let e=this.cache.get();if(!e?.refresh_token)throw new Error("No refresh token available");let t=await fetch(`https://${this.cfg.domain}/oauth2/token`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"refresh_token",client_id:this.cfg.clientId,refresh_token:e.refresh_token})});if(!t.ok)throw this.cache.clear(),this.setState({isAuthenticated:!1,isLoading:!1,user:void 0,error:void 0}),new Error("Token refresh failed \u2014 user must re-authenticate");let i=await t.json(),n=this.normaliseTokenResponse(i);return this.cache.set(n),this.setState({isAuthenticated:!0,isLoading:!1,user:this.buildUser(n),error:void 0}),n}normaliseTokenResponse(e){let t=typeof e.expires_in=="number"?e.expires_in:3600;return{access_token:e.access_token,id_token:e.id_token,refresh_token:e.refresh_token,scope:e.scope,expires_at:Math.floor(Date.now()/1e3)+t}}isExpired(e){return e.expires_at-M<Math.floor(Date.now()/1e3)}async loadStateFromCache(){let e=this.cache.get();if(!e){this.setState({isAuthenticated:!1,isLoading:!1,user:void 0,error:void 0});return}try{this.isExpired(e)&&await this.refreshTokens();let t=this.cache.get();this.setState({isAuthenticated:!0,isLoading:!1,user:t?this.buildUser(t):void 0,error:void 0})}catch{this.setState({isAuthenticated:!1,isLoading:!1,user:void 0,error:void 0})}}buildUser(e){if(e.id_token)try{let i={...y(e.id_token),access_token:e.access_token},n={sub:i.sub,name:i.name,email:i.email,email_verified:i.email_verified,picture:i.picture};for(let o of Object.keys(i))!(o in n)&&o!=="access_token"&&o!=="profile"&&(n[o]=i[o]);return i.profile=n,i}catch{return}}navigate(e){window.location.assign(e)}setState(e){this.state=e,this.notifyListeners(e)}notifyListeners(e){this.state=e,this.listeners.forEach(t=>t(e))}};var E=require("@angular/core"),h=new E.InjectionToken("AuthActionClientConfig");var c=class{constructor(e){this.client=new k(e),this._state$=new $.BehaviorSubject(this.client.getState()),this._unsub=this.client.onStateChange(t=>this._state$.next(t)),this.state$=this._state$.asObservable(),this.isAuthenticated$=this.state$.pipe((0,a.map)(t=>t.isAuthenticated),(0,a.distinctUntilChanged)()),this.user$=this.state$.pipe((0,a.map)(t=>t.user),(0,a.distinctUntilChanged)()),this.isLoading$=this.state$.pipe((0,a.map)(t=>t.isLoading),(0,a.distinctUntilChanged)()),this.error$=this.state$.pipe((0,a.map)(t=>t.error),(0,a.distinctUntilChanged)()),this.activeNavigator$=this.state$.pipe((0,a.map)(t=>t.activeNavigator),(0,a.distinctUntilChanged)())}loginWithRedirect(e){return this.client.loginWithRedirect(e)}loginWithPopup(e){return this.client.loginWithPopup(e)}handleRedirectCallback(e){return this.client.handleRedirectCallback(e)}getAccessToken(e){return this.client.getAccessToken(e)}logout(e){return this.client.logout(e)}removeUser(){this.client.removeUser()}get isAuthenticated(){return this._state$.value.isAuthenticated}get user(){return this._state$.value.user}ngOnDestroy(){this._unsub(),this._state$.complete()}};c=m([(0,v.Injectable)(),O(0,(0,v.Inject)(h))],c);var I=require("@angular/core"),N=require("@angular/core");function F(r){return(0,N.makeEnvironmentProviders)([{provide:h,useValue:r},c])}var d=class{static forRoot(e){return{ngModule:d,providers:[{provide:h,useValue:e},c]}}};d=m([(0,I.NgModule)()],d);var P=require("@angular/core"),W=require("@angular/router"),U=require("rxjs");var j=async()=>{let r=(0,P.inject)(c),e=(0,P.inject)(W.Router);return await(0,U.firstValueFrom)(r.isLoading$)&&await(0,U.firstValueFrom)(r.isLoading$),r.isAuthenticated?!0:(await r.loginWithRedirect({appState:{returnTo:e.getCurrentNavigation()?.extractedUrl.toString()}}),!1)};function G(r){let e=r?new URL(r).search:window.location.search,t=new URLSearchParams(e);return t.has("code")||t.has("error")}0&&(module.exports={AUTHACTION_CONFIG,AuthActionModule,AuthActionService,authActionGuard,hasAuthParams,provideAuthAction});
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/angular/index.ts","../../src/angular/service.ts","../../src/pkce.ts","../../src/storage.ts","../../src/client.ts","../../src/angular/token.ts","../../src/angular/module.ts","../../src/angular/guard.ts","../../src/utils.ts"],"sourcesContent":["export { AuthActionService } from './service';\nexport { AuthActionModule, provideAuthAction } from './module';\nexport { authActionGuard } from './guard';\nexport { AUTHACTION_CONFIG } from './token';\nexport { hasAuthParams } from '../utils';\n","import { Inject, Injectable, OnDestroy } from '@angular/core';\nimport { BehaviorSubject, Observable } from 'rxjs';\nimport { map, distinctUntilChanged } from 'rxjs/operators';\nimport { AuthActionClient } from '../client';\nimport {\n AuthActionClientConfig,\n AuthState,\n GetAccessTokenOptions,\n LoginOptions,\n LoginWithPopupOptions,\n LogoutOptions,\n RedirectCallbackResult,\n User,\n} from '../types';\nimport { AUTHACTION_CONFIG } from './token';\n\n@Injectable()\nexport class AuthActionService implements OnDestroy {\n private readonly client: AuthActionClient;\n private readonly _state$: BehaviorSubject<AuthState>;\n private readonly _unsub: () => void;\n\n // ── Observables ─────────────────────────────────────────────────────────────\n\n /** Full auth state stream */\n readonly state$: Observable<AuthState>;\n\n /** Emits true/false as authentication state changes */\n readonly isAuthenticated$: Observable<boolean>;\n\n /** Emits the decoded user profile, or undefined when not authenticated */\n readonly user$: Observable<User | undefined>;\n\n /** Emits true while the SDK is resolving the initial auth state */\n readonly isLoading$: Observable<boolean>;\n\n /** Emits the current error, or undefined when there is none */\n readonly error$: Observable<Error | undefined>;\n\n /** Emits which navigation is in-flight ('loginWithRedirect' | 'logout'), or undefined when idle */\n readonly activeNavigator$: Observable<'loginWithRedirect' | 'logout' | undefined>;\n\n constructor(@Inject(AUTHACTION_CONFIG) config: AuthActionClientConfig) {\n this.client = new AuthActionClient(config);\n this._state$ = new BehaviorSubject<AuthState>(this.client.getState());\n\n // Subscribe to client state changes and push into the Subject\n this._unsub = this.client.onStateChange((state) => this._state$.next(state));\n\n this.state$ = this._state$.asObservable();\n\n this.isAuthenticated$ = this.state$.pipe(\n map((s) => s.isAuthenticated),\n distinctUntilChanged(),\n );\n\n this.user$ = this.state$.pipe(\n map((s) => s.user),\n distinctUntilChanged(),\n );\n\n this.isLoading$ = this.state$.pipe(\n map((s) => s.isLoading),\n distinctUntilChanged(),\n );\n\n this.error$ = this.state$.pipe(\n map((s) => s.error),\n distinctUntilChanged(),\n );\n\n this.activeNavigator$ = this.state$.pipe(\n map((s) => s.activeNavigator),\n distinctUntilChanged(),\n );\n }\n\n // ── Auth methods ─────────────────────────────────────────────────────────────\n\n /** Redirect to AuthAction login page using PKCE authorization code flow */\n loginWithRedirect(options?: LoginOptions): Promise<void> {\n return this.client.loginWithRedirect(options);\n }\n\n /** Open AuthAction login in a popup window */\n loginWithPopup(options?: LoginWithPopupOptions): Promise<void> {\n return this.client.loginWithPopup(options);\n }\n\n /**\n * Handle the OAuth2 redirect callback.\n * Call this in the component or resolver mounted at your redirectUri route.\n */\n handleRedirectCallback(url?: string): Promise<RedirectCallbackResult> {\n return this.client.handleRedirectCallback(url);\n }\n\n /**\n * Get a valid access token, auto-refreshing if expired.\n * Use as the Bearer token for API calls:\n *\n * ```ts\n * const token = await this.auth.getAccessToken();\n * this.http.get('/api/files', {\n * headers: { Authorization: `Bearer ${token}` },\n * });\n * ```\n */\n getAccessToken(options?: GetAccessTokenOptions): Promise<string> {\n return this.client.getAccessToken(options);\n }\n\n /** Log out and optionally end the AuthAction SSO session */\n logout(options?: LogoutOptions): Promise<void> {\n return this.client.logout(options);\n }\n\n /**\n * Clear the local session without hitting the server's end-session endpoint.\n * Use this on 401 API responses where the server already invalidated the session.\n */\n removeUser(): void {\n this.client.removeUser();\n }\n\n // ── Snapshot accessors ────────────────────────────────────────────────────────\n\n /** Synchronous snapshot — use observables for reactive updates */\n get isAuthenticated(): boolean {\n return this._state$.value.isAuthenticated;\n }\n\n /** Synchronous snapshot — use user$ for reactive updates */\n get user(): User | undefined {\n return this._state$.value.user;\n }\n\n ngOnDestroy(): void {\n this._unsub();\n this._state$.complete();\n }\n}\n","/** Generate a cryptographically random string for PKCE code_verifier or state */\nexport function generateRandom(length = 64): string {\n const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~';\n const array = new Uint8Array(length);\n crypto.getRandomValues(array);\n return Array.from(array, (byte) => chars[byte % chars.length]).join('');\n}\n\n/** base64url-encode a buffer (no padding, url-safe) */\nfunction base64urlEncode(buffer: ArrayBuffer): string {\n return btoa(String.fromCharCode(...new Uint8Array(buffer)))\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=/g, '');\n}\n\n/** Compute the PKCE code_challenge from a code_verifier using S256 */\nexport async function generateCodeChallenge(verifier: string): Promise<string> {\n const encoded = new TextEncoder().encode(verifier);\n const digest = await crypto.subtle.digest('SHA-256', encoded);\n return base64urlEncode(digest);\n}\n\n/** Decode a JWT payload without verifying the signature */\nexport function decodeJwtPayload(token: string): Record<string, unknown> {\n const parts = token.split('.');\n if (parts.length !== 3) throw new Error('Invalid JWT format');\n const payload = parts[1].replace(/-/g, '+').replace(/_/g, '/');\n try {\n return JSON.parse(atob(payload));\n } catch {\n throw new Error('Failed to decode JWT payload');\n }\n}\n","import { TokenSet } from \"./types\";\n\n// ── PKCE transaction store ────────────────────────────────────────────────────\n// Must survive the redirect to /authorize and back, so always uses sessionStorage.\n\nconst PKCE_KEY = \"__authaction_pkce__\";\n\ninterface PkceTransaction {\n state: string;\n codeVerifier: string;\n redirectUri: string;\n appState?: Record<string, unknown>;\n}\n\nconst ss = (): Storage | null =>\n typeof sessionStorage !== \"undefined\" ? sessionStorage : null;\n\nexport const pkceStore = {\n save(tx: PkceTransaction): void {\n ss()?.setItem(PKCE_KEY, JSON.stringify(tx));\n },\n get(): PkceTransaction | null {\n const raw = ss()?.getItem(PKCE_KEY) ?? null;\n return raw ? (JSON.parse(raw) as PkceTransaction) : null;\n },\n clear(): void {\n ss()?.removeItem(PKCE_KEY);\n },\n};\n\n// ── Token cache ───────────────────────────────────────────────────────────────\n\nconst TOKEN_KEY = \"__authaction_tokens__\";\n\ntype StorageBackend = \"memory\" | \"localstorage\" | \"sessionstorage\";\n\nexport class TokenCache {\n private memStore: TokenSet | null = null;\n private backend: StorageBackend;\n\n constructor(backend: StorageBackend = \"memory\") {\n this.backend = backend;\n }\n\n get(): TokenSet | null {\n if (this.backend === \"memory\") return this.memStore;\n const raw = this.getStore()?.getItem(TOKEN_KEY) ?? null;\n return raw ? (JSON.parse(raw) as TokenSet) : null;\n }\n\n set(tokens: TokenSet): void {\n if (this.backend === \"memory\") {\n this.memStore = tokens;\n return;\n }\n this.getStore()?.setItem(TOKEN_KEY, JSON.stringify(tokens));\n }\n\n clear(): void {\n this.memStore = null;\n if (this.backend !== \"memory\") this.getStore()?.removeItem(TOKEN_KEY);\n }\n\n private getStore(): Storage | null {\n if (this.backend === \"localstorage\") {\n return typeof localStorage !== \"undefined\" ? localStorage : null;\n }\n return ss();\n }\n}\n","import { generateRandom, generateCodeChallenge, decodeJwtPayload } from './pkce';\nimport { pkceStore, TokenCache } from './storage';\nimport {\n AuthActionClientConfig,\n AuthState,\n GetAccessTokenOptions,\n LoginOptions,\n LoginWithPopupOptions,\n LogoutOptions,\n RedirectCallbackResult,\n StateChangeCallback,\n TokenSet,\n UnsubscribeFn,\n User,\n UserProfile,\n} from './types';\n\nconst DEFAULT_SCOPE = 'openid profile email';\n// Refresh the access token 60 seconds before it expires\nconst REFRESH_BUFFER_SECONDS = 60;\n\nexport class AuthActionClient {\n private readonly cfg: Required<Pick<AuthActionClientConfig, 'domain' | 'clientId' | 'redirectUri'>> &\n AuthActionClientConfig;\n private readonly cache: TokenCache;\n private readonly listeners = new Set<StateChangeCallback>();\n\n private state: AuthState = { isAuthenticated: false, isLoading: true, user: undefined, error: undefined };\n\n constructor(config: AuthActionClientConfig) {\n this.cfg = config as typeof this.cfg;\n this.cache = new TokenCache(config.cacheLocation ?? 'memory');\n // Restore state from cache on init (non-blocking)\n void this.loadStateFromCache();\n }\n\n // ── Login ────────────────────────────────────────────────────────────────────\n\n async loginWithRedirect(options: LoginOptions = {}): Promise<void> {\n this.setState({ ...this.state, activeNavigator: 'loginWithRedirect' });\n try {\n const state = generateRandom(32);\n const codeVerifier = generateRandom(64);\n const codeChallenge = await generateCodeChallenge(codeVerifier);\n\n pkceStore.save({\n state,\n codeVerifier,\n redirectUri: this.cfg.redirectUri,\n appState: options.appState,\n });\n\n const params = new URLSearchParams({\n response_type: 'code',\n client_id: this.cfg.clientId,\n redirect_uri: this.cfg.redirectUri,\n scope: this.cfg.scope ?? DEFAULT_SCOPE,\n state,\n code_challenge: codeChallenge,\n code_challenge_method: 'S256',\n ...this.cfg.authorizationParams,\n ...options.authorizationParams,\n });\n\n this.navigate(`https://${this.cfg.domain}/oauth2/authorize?${params}`);\n } catch (err) {\n this.setState({\n isAuthenticated: this.state.isAuthenticated,\n isLoading: false,\n user: this.state.user,\n error: err instanceof Error ? err : new Error(String(err)),\n });\n throw err;\n }\n }\n\n /** Opens AuthAction login in a popup window instead of a full redirect */\n async loginWithPopup(options: LoginWithPopupOptions = {}): Promise<void> {\n const state = generateRandom(32);\n const codeVerifier = generateRandom(64);\n const codeChallenge = await generateCodeChallenge(codeVerifier);\n\n pkceStore.save({ state, codeVerifier, redirectUri: this.cfg.redirectUri, appState: options.appState });\n\n const params = new URLSearchParams({\n response_type: 'code',\n client_id: this.cfg.clientId,\n redirect_uri: this.cfg.redirectUri,\n scope: this.cfg.scope ?? DEFAULT_SCOPE,\n state,\n code_challenge: codeChallenge,\n code_challenge_method: 'S256',\n ...this.cfg.authorizationParams,\n ...options.authorizationParams,\n });\n\n const popup = options.popup ?? window.open(\n `https://${this.cfg.domain}/oauth2/authorize?${params}`,\n 'authaction:login',\n 'width=480,height=640,left=100,top=100',\n );\n\n if (!popup) throw new Error('Popup was blocked. Allow popups for this site and try again.');\n\n await new Promise<void>((resolve, reject) => {\n const listener = async (event: MessageEvent) => {\n if (event.origin !== window.location.origin) return;\n if (!event.data?.type?.startsWith('authaction:')) return;\n\n window.removeEventListener('message', listener);\n\n if (event.data.type === 'authaction:callback') {\n try {\n const url = new URL(event.data.url);\n await this.exchangeCodeFromUrl(url);\n resolve();\n } catch (err) {\n reject(err);\n }\n } else if (event.data.type === 'authaction:error') {\n reject(new Error(event.data.error));\n }\n };\n window.addEventListener('message', listener);\n });\n }\n\n // ── Callback ─────────────────────────────────────────────────────────────────\n\n /**\n * Call this on the redirect callback page (e.g. /callback).\n * Exchanges the authorization code for tokens and returns the original appState.\n */\n async handleRedirectCallback(url?: string): Promise<RedirectCallbackResult> {\n const callbackUrl = new URL(url ?? window.location.href);\n const appState = await this.exchangeCodeFromUrl(callbackUrl);\n\n // Clean the code/state from the URL without a page reload\n const clean = new URL(window.location.href);\n clean.searchParams.delete('code');\n clean.searchParams.delete('state');\n window.history.replaceState({}, document.title, clean.toString());\n\n return { appState };\n }\n\n /**\n * Call this from the popup callback page to relay the URL back to the opener.\n * Place this call in the script that runs on your redirectUri page when in popup mode.\n */\n static handlePopupCallback(url?: string): void {\n if (!window.opener) return;\n window.opener.postMessage(\n { type: 'authaction:callback', url: url ?? window.location.href },\n window.location.origin,\n );\n window.close();\n }\n\n // ── Auth state ────────────────────────────────────────────────────────────────\n\n async isAuthenticated(): Promise<boolean> {\n const tokens = this.cache.get();\n if (!tokens) return false;\n // Auto-refresh if near expiry\n if (this.isExpired(tokens)) {\n try {\n await this.refreshTokens();\n } catch {\n return false;\n }\n }\n return true;\n }\n\n async getUser<T extends User = User>(): Promise<T | undefined> {\n if (!(await this.isAuthenticated())) return undefined;\n const tokens = this.cache.get();\n if (!tokens?.id_token) return undefined;\n try {\n return decodeJwtPayload(tokens.id_token) as unknown as T;\n } catch {\n return undefined;\n }\n }\n\n // ── Tokens ────────────────────────────────────────────────────────────────────\n\n /**\n * Returns a valid access token, refreshing automatically if needed.\n * Use this as the Bearer token for API calls including the AuthAction Files service.\n *\n * @example\n * const token = await client.getAccessToken();\n * fetch('/api/v1/files', { headers: { Authorization: `Bearer ${token}` } });\n */\n async getAccessToken(options: GetAccessTokenOptions = {}): Promise<string> {\n let tokens = this.cache.get();\n\n if (!tokens) throw new Error('Not authenticated — call loginWithRedirect() first');\n\n if (options.forceRefresh || this.isExpired(tokens)) {\n tokens = await this.refreshTokens();\n }\n\n return tokens.access_token;\n }\n\n // ── Logout ────────────────────────────────────────────────────────────────────\n\n async logout(options: LogoutOptions = {}): Promise<void> {\n this.cache.clear();\n\n if (options.federated === false) {\n this.setState({ isAuthenticated: false, isLoading: false, user: undefined, error: undefined });\n return;\n }\n\n this.setState({ isAuthenticated: false, isLoading: false, user: undefined, error: undefined, activeNavigator: 'logout' });\n\n const returnTo = options.returnTo ?? this.cfg.postLogoutRedirectUri;\n const params = new URLSearchParams({\n client_id: options.clientId ?? this.cfg.clientId,\n ...(returnTo ? { post_logout_redirect_uri: returnTo } : {}),\n });\n\n this.navigate(`https://${this.cfg.domain}/oidc/logout?${params}`);\n }\n\n /**\n * Clears the local session (tokens + state) without hitting the server's end-session endpoint.\n * Use this to handle 401 responses from APIs where the server already considers the session gone.\n */\n removeUser(): void {\n this.cache.clear();\n this.setState({ isAuthenticated: false, isLoading: false, user: undefined, error: undefined });\n }\n\n // ── State subscriptions ────────────────────────────────────────────────────────\n\n onStateChange(callback: StateChangeCallback): UnsubscribeFn {\n this.listeners.add(callback);\n // Emit current state immediately\n callback(this.state);\n return () => this.listeners.delete(callback);\n }\n\n getState(): AuthState {\n return { ...this.state };\n }\n\n // ── Private ───────────────────────────────────────────────────────────────────\n\n private async exchangeCodeFromUrl(url: URL): Promise<Record<string, unknown> | undefined> {\n const code = url.searchParams.get('code');\n const returnedState = url.searchParams.get('state');\n const error = url.searchParams.get('error');\n const errorDescription = url.searchParams.get('error_description');\n\n if (error) throw new Error(errorDescription ?? error);\n if (!code) throw new Error('No authorization code in callback URL');\n\n const tx = pkceStore.get();\n pkceStore.clear();\n\n if (!tx) throw new Error('No PKCE transaction found — did the login session expire?');\n if (tx.state !== returnedState) throw new Error('State mismatch — possible CSRF attack');\n\n const tokens = await this.exchangeCode(code, tx.codeVerifier, tx.redirectUri);\n this.cache.set(tokens);\n\n this.setState({ isAuthenticated: true, isLoading: false, user: this.buildUser(tokens), error: undefined });\n\n return tx.appState;\n }\n\n private async exchangeCode(code: string, codeVerifier: string, redirectUri: string): Promise<TokenSet> {\n const res = await fetch(`https://${this.cfg.domain}/oauth2/token`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n grant_type: 'authorization_code',\n client_id: this.cfg.clientId,\n code,\n redirect_uri: redirectUri,\n code_verifier: codeVerifier,\n }),\n });\n\n if (!res.ok) {\n const err = await res.json().catch(() => ({}));\n throw new Error(err.error_description ?? err.error ?? 'Token exchange failed');\n }\n\n const data = await res.json();\n return this.normaliseTokenResponse(data);\n }\n\n private async refreshTokens(): Promise<TokenSet> {\n const tokens = this.cache.get();\n if (!tokens?.refresh_token) throw new Error('No refresh token available');\n\n const res = await fetch(`https://${this.cfg.domain}/oauth2/token`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n grant_type: 'refresh_token',\n client_id: this.cfg.clientId,\n refresh_token: tokens.refresh_token,\n }),\n });\n\n if (!res.ok) {\n this.cache.clear();\n this.setState({ isAuthenticated: false, isLoading: false, user: undefined, error: undefined });\n throw new Error('Token refresh failed — user must re-authenticate');\n }\n\n const data = await res.json();\n const refreshed = this.normaliseTokenResponse(data);\n this.cache.set(refreshed);\n this.setState({ isAuthenticated: true, isLoading: false, user: this.buildUser(refreshed), error: undefined });\n return refreshed;\n }\n\n private normaliseTokenResponse(data: Record<string, unknown>): TokenSet {\n const expiresIn = typeof data.expires_in === 'number' ? data.expires_in : 3600;\n return {\n access_token: data.access_token as string,\n id_token: data.id_token as string | undefined,\n refresh_token: data.refresh_token as string | undefined,\n scope: data.scope as string | undefined,\n expires_at: Math.floor(Date.now() / 1000) + expiresIn,\n };\n }\n\n private isExpired(tokens: TokenSet): boolean {\n return tokens.expires_at - REFRESH_BUFFER_SECONDS < Math.floor(Date.now() / 1000);\n }\n\n private async loadStateFromCache(): Promise<void> {\n const tokens = this.cache.get();\n if (!tokens) {\n this.setState({ isAuthenticated: false, isLoading: false, user: undefined, error: undefined });\n return;\n }\n\n try {\n if (this.isExpired(tokens)) await this.refreshTokens();\n const cached = this.cache.get();\n this.setState({ isAuthenticated: true, isLoading: false, user: cached ? this.buildUser(cached) : undefined, error: undefined });\n } catch {\n this.setState({ isAuthenticated: false, isLoading: false, user: undefined, error: undefined });\n }\n }\n\n private buildUser(tokens: TokenSet): User | undefined {\n if (!tokens.id_token) return undefined;\n try {\n const claims = decodeJwtPayload(tokens.id_token) as User;\n const user: User = { ...claims, access_token: tokens.access_token };\n const profile: UserProfile = {\n sub: user.sub,\n name: user.name,\n email: user.email,\n email_verified: user.email_verified,\n picture: user.picture,\n };\n // Copy any extra claims from the ID token into profile\n for (const key of Object.keys(user)) {\n if (!(key in profile) && key !== 'access_token' && key !== 'profile') {\n profile[key] = user[key];\n }\n }\n user.profile = profile;\n return user;\n } catch {\n return undefined;\n }\n }\n\n /** Extracted for testability — spy on this method to capture redirect URLs */\n protected navigate(url: string): void {\n window.location.assign(url);\n }\n\n private setState(next: AuthState): void {\n this.state = next;\n this.notifyListeners(next);\n }\n\n private notifyListeners(state: AuthState): void {\n this.state = state;\n this.listeners.forEach((cb) => cb(state));\n }\n}\n","import { InjectionToken } from '@angular/core';\nimport { AuthActionClientConfig } from '../types';\n\nexport const AUTHACTION_CONFIG = new InjectionToken<AuthActionClientConfig>(\n 'AuthActionClientConfig',\n);\n","import { ModuleWithProviders, NgModule } from '@angular/core';\nimport { EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';\nimport { AuthActionClientConfig } from '../types';\nimport { AUTHACTION_CONFIG } from './token';\nimport { AuthActionService } from './service';\n\n/**\n * Provides AuthAction for standalone applications (Angular 14+).\n *\n * @example\n * ```ts\n * // main.ts\n * bootstrapApplication(AppComponent, {\n * providers: [\n * provideAuthAction({\n * domain: 'myapp.eu.authaction.com',\n * clientId: 'your-client-id',\n * redirectUri: 'http://localhost:4200/callback',\n * }),\n * ],\n * });\n * ```\n */\nexport function provideAuthAction(config: AuthActionClientConfig): EnvironmentProviders {\n return makeEnvironmentProviders([\n { provide: AUTHACTION_CONFIG, useValue: config },\n AuthActionService,\n ]);\n}\n\n/**\n * Angular module for module-based applications.\n *\n * @example\n * ```ts\n * // app.module.ts\n * @NgModule({\n * imports: [\n * AuthActionModule.forRoot({\n * domain: 'myapp.eu.authaction.com',\n * clientId: 'your-client-id',\n * redirectUri: 'http://localhost:4200/callback',\n * }),\n * ],\n * })\n * export class AppModule {}\n * ```\n */\n@NgModule()\nexport class AuthActionModule {\n static forRoot(config: AuthActionClientConfig): ModuleWithProviders<AuthActionModule> {\n return {\n ngModule: AuthActionModule,\n providers: [\n { provide: AUTHACTION_CONFIG, useValue: config },\n AuthActionService,\n ],\n };\n }\n}\n","import { inject } from '@angular/core';\nimport { CanActivateFn, Router } from '@angular/router';\nimport { firstValueFrom } from 'rxjs';\nimport { AuthActionService } from './service';\n\n/**\n * Route guard that redirects unauthenticated users to login.\n *\n * @example\n * ```ts\n * // app.routes.ts\n * {\n * path: 'dashboard',\n * canActivate: [authActionGuard],\n * component: DashboardComponent,\n * }\n * ```\n */\nexport const authActionGuard: CanActivateFn = async () => {\n const auth = inject(AuthActionService);\n const router = inject(Router);\n\n // Wait for the SDK to finish initialising before checking auth state\n const isLoading = await firstValueFrom(auth.isLoading$);\n if (isLoading) {\n await firstValueFrom(auth.isLoading$);\n }\n\n if (auth.isAuthenticated) return true;\n\n // Save the attempted URL so it can be restored after login\n await auth.loginWithRedirect({\n appState: { returnTo: router.getCurrentNavigation()?.extractedUrl.toString() },\n });\n\n return false;\n};\n","/**\n * Returns true if the given URL (or window.location) contains OAuth2 callback\n * params — i.e. the authorization server has redirected back with a code or error.\n * Mirrors react-oidc-context's hasAuthParams() for drop-in compatibility.\n */\nexport function hasAuthParams(url?: string): boolean {\n const search = url ? new URL(url).search : window.location.search;\n const params = new URLSearchParams(search);\n return params.has('code') || params.has('error');\n}\n"],"mappings":"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","names":["angular_exports","__export","AUTHACTION_CONFIG","AuthActionModule","AuthActionService","authActionGuard","hasAuthParams","provideAuthAction","__toCommonJS","import_core","import_rxjs","import_operators","generateRandom","length","chars","array","byte","base64urlEncode","buffer","generateCodeChallenge","verifier","encoded","digest","decodeJwtPayload","token","parts","payload","PKCE_KEY","ss","pkceStore","tx","raw","TOKEN_KEY","TokenCache","backend","tokens","DEFAULT_SCOPE","REFRESH_BUFFER_SECONDS","AuthActionClient","config","TokenCache","options","state","generateRandom","codeVerifier","codeChallenge","generateCodeChallenge","pkceStore","params","err","resolve","reject","listener","event","url","callbackUrl","appState","clean","tokens","decodeJwtPayload","returnTo","callback","code","returnedState","error","errorDescription","tx","redirectUri","res","data","refreshed","expiresIn","cached","user","profile","key","next","cb","import_core","AUTHACTION_CONFIG","AuthActionService","config","AuthActionClient","state","s","options","url","__decorateClass","__decorateParam","AUTHACTION_CONFIG","import_core","provideAuthAction","config","AUTHACTION_CONFIG","AuthActionService","AuthActionModule","__decorateClass","import_core","import_router","import_rxjs","authActionGuard","auth","AuthActionService","router","hasAuthParams","url","search","params"]}
@@ -0,0 +1,2 @@
1
+ import{a as u,b as l,c,d}from"../chunk-DHV7MPBE.mjs";import{Inject as g,Injectable as A}from"@angular/core";import{BehaviorSubject as v}from"rxjs";import{map as s,distinctUntilChanged as a}from"rxjs/operators";import{InjectionToken as m}from"@angular/core";var n=new m("AuthActionClientConfig");var i=class{constructor(t){this.client=new c(t),this._state$=new v(this.client.getState()),this._unsub=this.client.onStateChange(e=>this._state$.next(e)),this.state$=this._state$.asObservable(),this.isAuthenticated$=this.state$.pipe(s(e=>e.isAuthenticated),a()),this.user$=this.state$.pipe(s(e=>e.user),a()),this.isLoading$=this.state$.pipe(s(e=>e.isLoading),a()),this.error$=this.state$.pipe(s(e=>e.error),a()),this.activeNavigator$=this.state$.pipe(s(e=>e.activeNavigator),a())}loginWithRedirect(t){return this.client.loginWithRedirect(t)}loginWithPopup(t){return this.client.loginWithPopup(t)}handleRedirectCallback(t){return this.client.handleRedirectCallback(t)}getAccessToken(t){return this.client.getAccessToken(t)}logout(t){return this.client.logout(t)}removeUser(){this.client.removeUser()}get isAuthenticated(){return this._state$.value.isAuthenticated}get user(){return this._state$.value.user}ngOnDestroy(){this._unsub(),this._state$.complete()}};i=u([A(),l(0,g(n))],i);import{NgModule as f}from"@angular/core";import{makeEnvironmentProviders as C}from"@angular/core";function b(o){return C([{provide:n,useValue:o},i])}var r=class{static forRoot(t){return{ngModule:r,providers:[{provide:n,useValue:t},i]}}};r=u([f()],r);import{inject as h}from"@angular/core";import{Router as O}from"@angular/router";import{firstValueFrom as p}from"rxjs";var $=async()=>{let o=h(i),t=h(O);return await p(o.isLoading$)&&await p(o.isLoading$),o.isAuthenticated?!0:(await o.loginWithRedirect({appState:{returnTo:t.getCurrentNavigation()?.extractedUrl.toString()}}),!1)};export{n as AUTHACTION_CONFIG,r as AuthActionModule,i as AuthActionService,$ as authActionGuard,d as hasAuthParams,b as provideAuthAction};
2
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/angular/service.ts","../../src/angular/token.ts","../../src/angular/module.ts","../../src/angular/guard.ts"],"sourcesContent":["import { Inject, Injectable, OnDestroy } from '@angular/core';\nimport { BehaviorSubject, Observable } from 'rxjs';\nimport { map, distinctUntilChanged } from 'rxjs/operators';\nimport { AuthActionClient } from '../client';\nimport {\n AuthActionClientConfig,\n AuthState,\n GetAccessTokenOptions,\n LoginOptions,\n LoginWithPopupOptions,\n LogoutOptions,\n RedirectCallbackResult,\n User,\n} from '../types';\nimport { AUTHACTION_CONFIG } from './token';\n\n@Injectable()\nexport class AuthActionService implements OnDestroy {\n private readonly client: AuthActionClient;\n private readonly _state$: BehaviorSubject<AuthState>;\n private readonly _unsub: () => void;\n\n // ── Observables ─────────────────────────────────────────────────────────────\n\n /** Full auth state stream */\n readonly state$: Observable<AuthState>;\n\n /** Emits true/false as authentication state changes */\n readonly isAuthenticated$: Observable<boolean>;\n\n /** Emits the decoded user profile, or undefined when not authenticated */\n readonly user$: Observable<User | undefined>;\n\n /** Emits true while the SDK is resolving the initial auth state */\n readonly isLoading$: Observable<boolean>;\n\n /** Emits the current error, or undefined when there is none */\n readonly error$: Observable<Error | undefined>;\n\n /** Emits which navigation is in-flight ('loginWithRedirect' | 'logout'), or undefined when idle */\n readonly activeNavigator$: Observable<'loginWithRedirect' | 'logout' | undefined>;\n\n constructor(@Inject(AUTHACTION_CONFIG) config: AuthActionClientConfig) {\n this.client = new AuthActionClient(config);\n this._state$ = new BehaviorSubject<AuthState>(this.client.getState());\n\n // Subscribe to client state changes and push into the Subject\n this._unsub = this.client.onStateChange((state) => this._state$.next(state));\n\n this.state$ = this._state$.asObservable();\n\n this.isAuthenticated$ = this.state$.pipe(\n map((s) => s.isAuthenticated),\n distinctUntilChanged(),\n );\n\n this.user$ = this.state$.pipe(\n map((s) => s.user),\n distinctUntilChanged(),\n );\n\n this.isLoading$ = this.state$.pipe(\n map((s) => s.isLoading),\n distinctUntilChanged(),\n );\n\n this.error$ = this.state$.pipe(\n map((s) => s.error),\n distinctUntilChanged(),\n );\n\n this.activeNavigator$ = this.state$.pipe(\n map((s) => s.activeNavigator),\n distinctUntilChanged(),\n );\n }\n\n // ── Auth methods ─────────────────────────────────────────────────────────────\n\n /** Redirect to AuthAction login page using PKCE authorization code flow */\n loginWithRedirect(options?: LoginOptions): Promise<void> {\n return this.client.loginWithRedirect(options);\n }\n\n /** Open AuthAction login in a popup window */\n loginWithPopup(options?: LoginWithPopupOptions): Promise<void> {\n return this.client.loginWithPopup(options);\n }\n\n /**\n * Handle the OAuth2 redirect callback.\n * Call this in the component or resolver mounted at your redirectUri route.\n */\n handleRedirectCallback(url?: string): Promise<RedirectCallbackResult> {\n return this.client.handleRedirectCallback(url);\n }\n\n /**\n * Get a valid access token, auto-refreshing if expired.\n * Use as the Bearer token for API calls:\n *\n * ```ts\n * const token = await this.auth.getAccessToken();\n * this.http.get('/api/files', {\n * headers: { Authorization: `Bearer ${token}` },\n * });\n * ```\n */\n getAccessToken(options?: GetAccessTokenOptions): Promise<string> {\n return this.client.getAccessToken(options);\n }\n\n /** Log out and optionally end the AuthAction SSO session */\n logout(options?: LogoutOptions): Promise<void> {\n return this.client.logout(options);\n }\n\n /**\n * Clear the local session without hitting the server's end-session endpoint.\n * Use this on 401 API responses where the server already invalidated the session.\n */\n removeUser(): void {\n this.client.removeUser();\n }\n\n // ── Snapshot accessors ────────────────────────────────────────────────────────\n\n /** Synchronous snapshot — use observables for reactive updates */\n get isAuthenticated(): boolean {\n return this._state$.value.isAuthenticated;\n }\n\n /** Synchronous snapshot — use user$ for reactive updates */\n get user(): User | undefined {\n return this._state$.value.user;\n }\n\n ngOnDestroy(): void {\n this._unsub();\n this._state$.complete();\n }\n}\n","import { InjectionToken } from '@angular/core';\nimport { AuthActionClientConfig } from '../types';\n\nexport const AUTHACTION_CONFIG = new InjectionToken<AuthActionClientConfig>(\n 'AuthActionClientConfig',\n);\n","import { ModuleWithProviders, NgModule } from '@angular/core';\nimport { EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';\nimport { AuthActionClientConfig } from '../types';\nimport { AUTHACTION_CONFIG } from './token';\nimport { AuthActionService } from './service';\n\n/**\n * Provides AuthAction for standalone applications (Angular 14+).\n *\n * @example\n * ```ts\n * // main.ts\n * bootstrapApplication(AppComponent, {\n * providers: [\n * provideAuthAction({\n * domain: 'myapp.eu.authaction.com',\n * clientId: 'your-client-id',\n * redirectUri: 'http://localhost:4200/callback',\n * }),\n * ],\n * });\n * ```\n */\nexport function provideAuthAction(config: AuthActionClientConfig): EnvironmentProviders {\n return makeEnvironmentProviders([\n { provide: AUTHACTION_CONFIG, useValue: config },\n AuthActionService,\n ]);\n}\n\n/**\n * Angular module for module-based applications.\n *\n * @example\n * ```ts\n * // app.module.ts\n * @NgModule({\n * imports: [\n * AuthActionModule.forRoot({\n * domain: 'myapp.eu.authaction.com',\n * clientId: 'your-client-id',\n * redirectUri: 'http://localhost:4200/callback',\n * }),\n * ],\n * })\n * export class AppModule {}\n * ```\n */\n@NgModule()\nexport class AuthActionModule {\n static forRoot(config: AuthActionClientConfig): ModuleWithProviders<AuthActionModule> {\n return {\n ngModule: AuthActionModule,\n providers: [\n { provide: AUTHACTION_CONFIG, useValue: config },\n AuthActionService,\n ],\n };\n }\n}\n","import { inject } from '@angular/core';\nimport { CanActivateFn, Router } from '@angular/router';\nimport { firstValueFrom } from 'rxjs';\nimport { AuthActionService } from './service';\n\n/**\n * Route guard that redirects unauthenticated users to login.\n *\n * @example\n * ```ts\n * // app.routes.ts\n * {\n * path: 'dashboard',\n * canActivate: [authActionGuard],\n * component: DashboardComponent,\n * }\n * ```\n */\nexport const authActionGuard: CanActivateFn = async () => {\n const auth = inject(AuthActionService);\n const router = inject(Router);\n\n // Wait for the SDK to finish initialising before checking auth state\n const isLoading = await firstValueFrom(auth.isLoading$);\n if (isLoading) {\n await firstValueFrom(auth.isLoading$);\n }\n\n if (auth.isAuthenticated) return true;\n\n // Save the attempted URL so it can be restored after login\n await auth.loginWithRedirect({\n appState: { returnTo: router.getCurrentNavigation()?.extractedUrl.toString() },\n });\n\n return false;\n};\n"],"mappings":"qDAAA,OAAS,UAAAA,EAAQ,cAAAC,MAA6B,gBAC9C,OAAS,mBAAAC,MAAmC,OAC5C,OAAS,OAAAC,EAAK,wBAAAC,MAA4B,iBCF1C,OAAS,kBAAAC,MAAsB,gBAGxB,IAAMC,EAAoB,IAAID,EACnC,wBACF,EDYO,IAAME,EAAN,KAA6C,CAyBlD,YAAuCC,EAAgC,CACrE,KAAK,OAAS,IAAIC,EAAiBD,CAAM,EACzC,KAAK,QAAU,IAAIE,EAA2B,KAAK,OAAO,SAAS,CAAC,EAGpE,KAAK,OAAS,KAAK,OAAO,cAAeC,GAAU,KAAK,QAAQ,KAAKA,CAAK,CAAC,EAE3E,KAAK,OAAS,KAAK,QAAQ,aAAa,EAExC,KAAK,iBAAmB,KAAK,OAAO,KAClCC,EAAKC,GAAMA,EAAE,eAAe,EAC5BC,EAAqB,CACvB,EAEA,KAAK,MAAQ,KAAK,OAAO,KACvBF,EAAKC,GAAMA,EAAE,IAAI,EACjBC,EAAqB,CACvB,EAEA,KAAK,WAAa,KAAK,OAAO,KAC5BF,EAAKC,GAAMA,EAAE,SAAS,EACtBC,EAAqB,CACvB,EAEA,KAAK,OAAS,KAAK,OAAO,KACxBF,EAAKC,GAAMA,EAAE,KAAK,EAClBC,EAAqB,CACvB,EAEA,KAAK,iBAAmB,KAAK,OAAO,KAClCF,EAAKC,GAAMA,EAAE,eAAe,EAC5BC,EAAqB,CACvB,CACF,CAKA,kBAAkBC,EAAuC,CACvD,OAAO,KAAK,OAAO,kBAAkBA,CAAO,CAC9C,CAGA,eAAeA,EAAgD,CAC7D,OAAO,KAAK,OAAO,eAAeA,CAAO,CAC3C,CAMA,uBAAuBC,EAA+C,CACpE,OAAO,KAAK,OAAO,uBAAuBA,CAAG,CAC/C,CAaA,eAAeD,EAAkD,CAC/D,OAAO,KAAK,OAAO,eAAeA,CAAO,CAC3C,CAGA,OAAOA,EAAwC,CAC7C,OAAO,KAAK,OAAO,OAAOA,CAAO,CACnC,CAMA,YAAmB,CACjB,KAAK,OAAO,WAAW,CACzB,CAKA,IAAI,iBAA2B,CAC7B,OAAO,KAAK,QAAQ,MAAM,eAC5B,CAGA,IAAI,MAAyB,CAC3B,OAAO,KAAK,QAAQ,MAAM,IAC5B,CAEA,aAAoB,CAClB,KAAK,OAAO,EACZ,KAAK,QAAQ,SAAS,CACxB,CACF,EA5HaR,EAANU,EAAA,CADNC,EAAW,EA0BGC,EAAA,EAAAC,EAAOC,CAAiB,IAzB1Bd,GEjBb,OAA8B,YAAAe,MAAgB,gBAC9C,OAA+B,4BAAAC,MAAgC,gBAsBxD,SAASC,EAAkBC,EAAsD,CACtF,OAAOC,EAAyB,CAC9B,CAAE,QAASC,EAAmB,SAAUF,CAAO,EAC/CG,CACF,CAAC,CACH,CAqBO,IAAMC,EAAN,KAAuB,CAC5B,OAAO,QAAQJ,EAAuE,CACpF,MAAO,CACL,SAAUI,EACV,UAAW,CACT,CAAE,QAASF,EAAmB,SAAUF,CAAO,EAC/CG,CACF,CACF,CACF,CACF,EAVaC,EAANC,EAAA,CADNC,EAAS,GACGF,GCjDb,OAAS,UAAAG,MAAc,gBACvB,OAAwB,UAAAC,MAAc,kBACtC,OAAS,kBAAAC,MAAsB,OAgBxB,IAAMC,EAAiC,SAAY,CACxD,IAAMC,EAAOC,EAAOC,CAAiB,EAC/BC,EAASF,EAAOG,CAAM,EAQ5B,OALkB,MAAMC,EAAeL,EAAK,UAAU,GAEpD,MAAMK,EAAeL,EAAK,UAAU,EAGlCA,EAAK,gBAAwB,IAGjC,MAAMA,EAAK,kBAAkB,CAC3B,SAAU,CAAE,SAAUG,EAAO,qBAAqB,GAAG,aAAa,SAAS,CAAE,CAC/E,CAAC,EAEM,GACT","names":["Inject","Injectable","BehaviorSubject","map","distinctUntilChanged","InjectionToken","AUTHACTION_CONFIG","AuthActionService","config","AuthActionClient","BehaviorSubject","state","map","s","distinctUntilChanged","options","url","__decorateClass","Injectable","__decorateParam","Inject","AUTHACTION_CONFIG","NgModule","makeEnvironmentProviders","provideAuthAction","config","makeEnvironmentProviders","AUTHACTION_CONFIG","AuthActionService","AuthActionModule","__decorateClass","NgModule","inject","Router","firstValueFrom","authActionGuard","auth","inject","AuthActionService","router","Router","firstValueFrom"]}
@@ -0,0 +1,2 @@
1
+ import{c as l}from"./chunk-DHV7MPBE.mjs";import{createContext as d,useContext as g,useEffect as p,useMemo as m,useState as C}from"react";import{jsx as P}from"react/jsx-runtime";var h=d(null);function R({children:t,onRedirectCallback:s,...i}){let o=m(()=>new l(i),[i.domain,i.clientId,i.redirectUri]),[u,a]=C(o.getState());return p(()=>o.onStateChange(a),[o]),p(()=>{let c=new URLSearchParams(window.location.search);!c.has("code")&&!c.has("error")||o.handleRedirectCallback().then(({appState:A})=>s?.(A)).catch(()=>{})},[]),P(h.Provider,{value:{client:o,state:u},children:t})}function r(){let t=g(h);if(!t)throw new Error('useAuthAction() must be used inside <AuthActionProvider>. Wrap your application root with <AuthActionProvider domain="..." clientId="..." redirectUri="...">.');return t}import{useCallback as n}from"react";function U(){let{client:t,state:s}=r(),i=n(e=>t.loginWithRedirect(e),[t]),o=n(e=>t.loginWithPopup(e),[t]),u=n(e=>t.handleRedirectCallback(e),[t]),a=n(e=>t.getAccessToken(e),[t]),c=n(e=>t.logout(e),[t]),A=n(()=>t.removeUser(),[t]);return{...s,loginWithRedirect:i,loginWithPopup:o,handleRedirectCallback:u,getAccessToken:a,logout:c,removeUser:A}}function b(){return r().state.user}function S(){return r().state.isLoading}function W(){return r().state.isAuthenticated}function w(){let{client:t}=r();return n(s=>t.getAccessToken(s),[t])}export{R as a,U as b,b as c,S as d,W as e,w as f};
2
+ //# sourceMappingURL=chunk-5RMD5KG6.mjs.map