@authdog/angular 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/README.md ADDED
@@ -0,0 +1,117 @@
1
+ # `@authdog/angular`
2
+
3
+ **Authdog SDK for Angular** — a root-provided auth service backed by signals, a
4
+ functional HTTP interceptor, and a route guard. Integrates natively with
5
+ Angular's dependency injection and `HttpClient`.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ bun add @authdog/angular
11
+ ```
12
+
13
+ ```bash
14
+ npm install @authdog/angular
15
+ ```
16
+
17
+ Your public key (`pk_…`) is available in the [Authdog dashboard](https://authdog.com).
18
+ It is safe to expose to the browser.
19
+
20
+ ## Setup
21
+
22
+ Register the SDK in your standalone application config:
23
+
24
+ ```ts
25
+ import { ApplicationConfig } from "@angular/core";
26
+ import { provideHttpClient, withInterceptors } from "@angular/common/http";
27
+ import { provideAuthdog, authdogInterceptor } from "@authdog/angular";
28
+
29
+ export const appConfig: ApplicationConfig = {
30
+ providers: [
31
+ provideAuthdog({ publicKey: "pk_xxxxxxxxxxxxxxxx" }),
32
+ // Attaches `Authorization: Bearer <token>` to outgoing requests.
33
+ provideHttpClient(withInterceptors([authdogInterceptor])),
34
+ ],
35
+ };
36
+ ```
37
+
38
+ ## Usage
39
+
40
+ Inject `AuthdogService` anywhere. Its state is exposed as **signals**, so it
41
+ works seamlessly in templates and `computed()`s:
42
+
43
+ ```ts
44
+ import { Component, inject } from "@angular/core";
45
+ import { AuthdogService } from "@authdog/angular";
46
+
47
+ @Component({
48
+ selector: "app-profile",
49
+ standalone: true,
50
+ template: `
51
+ @if (auth.isLoading()) {
52
+ <p>Loading…</p>
53
+ } @else if (auth.token()) {
54
+ <p>Signed in</p>
55
+ <button (click)="auth.signOut()">Sign out</button>
56
+ } @else {
57
+ <button (click)="auth.signIn()">Sign in</button>
58
+ <button (click)="auth.signUp()">Sign up</button>
59
+ }
60
+ `,
61
+ })
62
+ export class ProfileComponent {
63
+ readonly auth = inject(AuthdogService);
64
+
65
+ async ngOnInit() {
66
+ await this.auth.fetchUser();
67
+ console.log(this.auth.user());
68
+ }
69
+ }
70
+ ```
71
+
72
+ ### Signals & methods
73
+
74
+ | Member | Type | Description |
75
+ | ------------------ | -------------------------- | ------------------------------------------------- |
76
+ | `token` | `Signal<string \| null>` | Current bearer token. |
77
+ | `isLoading` | `Signal<boolean>` | True during the initial bootstrap. |
78
+ | `isAuthenticated` | `Signal<boolean>` | True when a token **and** a user are present. |
79
+ | `user` | `Signal<unknown>` | Last fetched userinfo payload. |
80
+ | `error` | `Signal<Error \| null>` | Last sign-in / fetch error. |
81
+ | `signIn(pk?, url?)`| `void` | Redirect to hosted sign-in. |
82
+ | `signUp(pk?, url?)`| `void` | Redirect to hosted sign-up (`prompt=signup`). |
83
+ | `signOut()` | `void` | Clear the session and redirect to `/logout`. |
84
+ | `fetchUser(pk?)` | `Promise<unknown>` | Load the current user from `userinfo`. |
85
+
86
+ `publicKey` defaults to the value passed to `provideAuthdog`, so the argument is
87
+ optional in most calls.
88
+
89
+ ### Route guard
90
+
91
+ ```ts
92
+ import { Routes } from "@angular/router";
93
+ import { authdogGuard } from "@authdog/angular";
94
+
95
+ export const routes: Routes = [
96
+ { path: "dashboard", component: DashboardComponent, canActivate: [authdogGuard] },
97
+ ];
98
+ ```
99
+
100
+ > ⚠️ **`authdogGuard` is presentational / UX only — it is _not_ a security
101
+ > boundary.** It runs in the browser and is trivially bypassable. Every
102
+ > protected operation must be independently enforced server-side; the API
103
+ > behind a guarded route must validate the session on every request.
104
+
105
+ ## How it works
106
+
107
+ On startup the service reads a `?token=` value from the URL, validates it
108
+ against the JWT pattern **before** persisting it to `localStorage`, then strips
109
+ it from the address bar via `history.replaceState`. Otherwise it restores any
110
+ previously stored token. All `window` / `localStorage` access is guarded so the
111
+ service is inert under Angular Universal (SSR). Public keys are decoded through
112
+ the hardened `@authdog/node-commons` parser, which enforces a trusted
113
+ identity-host allowlist (SSRF / token-exfiltration protection).
114
+
115
+ ## License
116
+
117
+ MIT
@@ -0,0 +1,192 @@
1
+ import { InjectionToken, EnvironmentProviders, Signal } from '@angular/core';
2
+ import { HttpInterceptorFn } from '@angular/common/http';
3
+ import { CanActivateFn } from '@angular/router';
4
+ import { PublicKeyPayload } from '@authdog/node-commons';
5
+ export { PublicKeyPayload } from '@authdog/node-commons';
6
+
7
+ interface AuthdogConfig {
8
+ /**
9
+ * Authdog public key (`pk_…`). Safe to expose to the browser. Used as the
10
+ * `client_id` and to derive the identity host / environment for OIDC flows.
11
+ */
12
+ publicKey: string;
13
+ /**
14
+ * Path the route guard redirects to when the user is not authenticated.
15
+ * Defaults to `/`.
16
+ */
17
+ loginPath?: string;
18
+ }
19
+ /** DI token carrying the SDK configuration provided via `provideAuthdog`. */
20
+ declare const AUTHDOG_CONFIG: InjectionToken<AuthdogConfig>;
21
+
22
+ /**
23
+ * Wires the Authdog SDK into a standalone Angular application.
24
+ *
25
+ * Add to `ApplicationConfig.providers`:
26
+ *
27
+ * ```ts
28
+ * export const appConfig: ApplicationConfig = {
29
+ * providers: [
30
+ * provideAuthdog({ publicKey: environment.authdogPublicKey }),
31
+ * provideHttpClient(withInterceptors([authdogInterceptor])),
32
+ * ],
33
+ * };
34
+ * ```
35
+ *
36
+ * Note: the HTTP interceptor is registered by the consumer via
37
+ * `withInterceptors([authdogInterceptor])` so it composes with the app's own
38
+ * `provideHttpClient` setup rather than forcing a particular configuration.
39
+ */
40
+ declare const provideAuthdog: (config: AuthdogConfig) => EnvironmentProviders;
41
+
42
+ /**
43
+ * Root-provided service that owns the client-side auth state.
44
+ *
45
+ * State is exposed as readonly signals so templates and `computed`s react to
46
+ * changes automatically. The service is browser-first: every `window` /
47
+ * `localStorage` access is guarded so it is inert under Angular Universal
48
+ * (SSR), where it simply reports "not loading, no token".
49
+ */
50
+ declare class AuthdogService {
51
+ private readonly config;
52
+ private readonly _token;
53
+ private readonly _isLoading;
54
+ private readonly _user;
55
+ private readonly _error;
56
+ /** Current bearer token, or `null` when signed out. */
57
+ readonly token: Signal<string | null>;
58
+ /** True until the initial token-from-URL / localStorage bootstrap finishes. */
59
+ readonly isLoading: Signal<boolean>;
60
+ /** The last fetched userinfo `user` payload, or `null`. */
61
+ readonly user: Signal<unknown>;
62
+ /** The last error raised by a sign-in / fetch operation, or `null`. */
63
+ readonly error: Signal<Error | null>;
64
+ /** True when a token is present AND a user has been loaded. */
65
+ readonly isAuthenticated: Signal<boolean>;
66
+ constructor();
67
+ /**
68
+ * Reads a token from the URL (`?token=`) or localStorage. A URL token is
69
+ * validated against the JWT pattern BEFORE it is persisted, so arbitrary
70
+ * attacker-supplied query data is never written to storage, and is stripped
71
+ * from the address bar via `history.replaceState` regardless of validity.
72
+ */
73
+ private bootstrap;
74
+ /** Imperatively set (or clear) the in-memory + persisted token. */
75
+ setToken(token: string | null): void;
76
+ /** Redirect the browser to the hosted sign-in page. */
77
+ signIn(publicKey?: string, redirectUrl?: string): void;
78
+ /** Redirect the browser to the hosted sign-up page (`prompt=signup`). */
79
+ signUp(publicKey?: string, redirectUrl?: string): void;
80
+ /** Clear the session locally and redirect to the logout endpoint. */
81
+ signOut(): void;
82
+ /**
83
+ * Fetches the current user from the identity host's userinfo endpoint and
84
+ * stores it on the `user` signal. Returns `null` when there is no token.
85
+ */
86
+ fetchUser(publicKey?: string): Promise<unknown>;
87
+ }
88
+
89
+ /**
90
+ * Functional HTTP interceptor that attaches `Authorization: Bearer <token>`
91
+ * to outgoing requests when a session token is present. Register it with
92
+ * `provideAuthdog()` or directly via `provideHttpClient(withInterceptors([authdogInterceptor]))`.
93
+ *
94
+ * Requests that already carry an `Authorization` header are left untouched.
95
+ */
96
+ declare const authdogInterceptor: HttpInterceptorFn;
97
+
98
+ /**
99
+ * ⚠️ PRESENTATIONAL / UX ONLY — NOT A SECURITY BOUNDARY.
100
+ *
101
+ * This guard prevents an unauthenticated user from *navigating* to a route in
102
+ * the SPA so they see a login redirect instead of a broken page. It runs
103
+ * entirely in the browser and is therefore trivially bypassable by any client
104
+ * (devtools, crafted requests, a patched bundle). It MUST NOT be relied on to
105
+ * protect data or actions.
106
+ *
107
+ * Every protected operation MUST be independently enforced server-side: the
108
+ * API behind the route has to validate the session on each request regardless
109
+ * of what this guard decides.
110
+ */
111
+ declare const authdogGuard: CanActivateFn;
112
+
113
+ /** Shared localStorage key for the persisted token. */
114
+ declare const TOKEN_STORAGE_KEY = "token";
115
+ /** JWT shape: three base64url segments separated by dots. */
116
+ declare const JWT_PATTERN: RegExp;
117
+ declare const getTokenFromUri: (url: string) => string | null;
118
+ interface IFetchUserData {
119
+ user: {
120
+ id: string;
121
+ environmentId: string;
122
+ externalId: string;
123
+ userName: string;
124
+ displayName: string;
125
+ nickName: string;
126
+ profileUrl: string;
127
+ title: string;
128
+ userType: string;
129
+ preferredLanguage: string | null;
130
+ locale: string | null;
131
+ timezone: string | null;
132
+ active: boolean;
133
+ provider: string;
134
+ lastLogin: string;
135
+ createdAt: string;
136
+ updatedAt: string;
137
+ names: {
138
+ id: string;
139
+ userId: string;
140
+ formatted: string | null;
141
+ familyName: string;
142
+ givenName: string;
143
+ middleName: string | null;
144
+ honorificPrefix: string | null;
145
+ honorificSuffix: string | null;
146
+ createdAt: string;
147
+ updatedAt: string;
148
+ };
149
+ addresses: [];
150
+ emails: {
151
+ value: string;
152
+ primary: boolean;
153
+ type: string;
154
+ }[];
155
+ phoneNumbers: [];
156
+ ims: [];
157
+ photos: {
158
+ value: string;
159
+ type: string;
160
+ }[];
161
+ };
162
+ meta: {
163
+ code: number;
164
+ message: string;
165
+ };
166
+ }
167
+ /**
168
+ * Fetches user data from the identity host's OIDC `userinfo` endpoint. The
169
+ * identity host is decoded through the hardened public-key parser, which
170
+ * enforces the trusted-host allowlist before the bearer token is ever sent.
171
+ */
172
+ declare const fetchUserData: (publicKey: string, token: string) => Promise<IFetchUserData | null>;
173
+ /**
174
+ * Builds the OIDC authorize URL for a sign-in or sign-up redirect. The public
175
+ * key is decoded through the hardened parser (no raw base64/JSON) so an
176
+ * untrusted identity host can never be used as the redirect target.
177
+ */
178
+ declare const buildAuthorizeUrl: (publicKey: string, redirectUri: string, options?: {
179
+ signup?: boolean;
180
+ }) => string;
181
+
182
+ /**
183
+ * Decodes and validates an Authdog public key. Delegates to the hardened
184
+ * shared parser in @authdog/node-commons, which validates the payload and
185
+ * enforces a trusted identity-host allowlist (SSRF / token-exfiltration
186
+ * protection) rather than blindly decoding base64/JSON.
187
+ */
188
+ declare const getPublicKeyPayload: (publicKey: string) => PublicKeyPayload;
189
+ /** Lightweight, fast-fail shape check before the full decode. */
190
+ declare const validatePublicKey: (publicKey: string) => void;
191
+
192
+ export { AUTHDOG_CONFIG, type AuthdogConfig, AuthdogService, type IFetchUserData, JWT_PATTERN, TOKEN_STORAGE_KEY, authdogGuard, authdogInterceptor, buildAuthorizeUrl, fetchUserData, getPublicKeyPayload, getTokenFromUri, provideAuthdog, validatePublicKey };
@@ -0,0 +1,192 @@
1
+ import { InjectionToken, EnvironmentProviders, Signal } from '@angular/core';
2
+ import { HttpInterceptorFn } from '@angular/common/http';
3
+ import { CanActivateFn } from '@angular/router';
4
+ import { PublicKeyPayload } from '@authdog/node-commons';
5
+ export { PublicKeyPayload } from '@authdog/node-commons';
6
+
7
+ interface AuthdogConfig {
8
+ /**
9
+ * Authdog public key (`pk_…`). Safe to expose to the browser. Used as the
10
+ * `client_id` and to derive the identity host / environment for OIDC flows.
11
+ */
12
+ publicKey: string;
13
+ /**
14
+ * Path the route guard redirects to when the user is not authenticated.
15
+ * Defaults to `/`.
16
+ */
17
+ loginPath?: string;
18
+ }
19
+ /** DI token carrying the SDK configuration provided via `provideAuthdog`. */
20
+ declare const AUTHDOG_CONFIG: InjectionToken<AuthdogConfig>;
21
+
22
+ /**
23
+ * Wires the Authdog SDK into a standalone Angular application.
24
+ *
25
+ * Add to `ApplicationConfig.providers`:
26
+ *
27
+ * ```ts
28
+ * export const appConfig: ApplicationConfig = {
29
+ * providers: [
30
+ * provideAuthdog({ publicKey: environment.authdogPublicKey }),
31
+ * provideHttpClient(withInterceptors([authdogInterceptor])),
32
+ * ],
33
+ * };
34
+ * ```
35
+ *
36
+ * Note: the HTTP interceptor is registered by the consumer via
37
+ * `withInterceptors([authdogInterceptor])` so it composes with the app's own
38
+ * `provideHttpClient` setup rather than forcing a particular configuration.
39
+ */
40
+ declare const provideAuthdog: (config: AuthdogConfig) => EnvironmentProviders;
41
+
42
+ /**
43
+ * Root-provided service that owns the client-side auth state.
44
+ *
45
+ * State is exposed as readonly signals so templates and `computed`s react to
46
+ * changes automatically. The service is browser-first: every `window` /
47
+ * `localStorage` access is guarded so it is inert under Angular Universal
48
+ * (SSR), where it simply reports "not loading, no token".
49
+ */
50
+ declare class AuthdogService {
51
+ private readonly config;
52
+ private readonly _token;
53
+ private readonly _isLoading;
54
+ private readonly _user;
55
+ private readonly _error;
56
+ /** Current bearer token, or `null` when signed out. */
57
+ readonly token: Signal<string | null>;
58
+ /** True until the initial token-from-URL / localStorage bootstrap finishes. */
59
+ readonly isLoading: Signal<boolean>;
60
+ /** The last fetched userinfo `user` payload, or `null`. */
61
+ readonly user: Signal<unknown>;
62
+ /** The last error raised by a sign-in / fetch operation, or `null`. */
63
+ readonly error: Signal<Error | null>;
64
+ /** True when a token is present AND a user has been loaded. */
65
+ readonly isAuthenticated: Signal<boolean>;
66
+ constructor();
67
+ /**
68
+ * Reads a token from the URL (`?token=`) or localStorage. A URL token is
69
+ * validated against the JWT pattern BEFORE it is persisted, so arbitrary
70
+ * attacker-supplied query data is never written to storage, and is stripped
71
+ * from the address bar via `history.replaceState` regardless of validity.
72
+ */
73
+ private bootstrap;
74
+ /** Imperatively set (or clear) the in-memory + persisted token. */
75
+ setToken(token: string | null): void;
76
+ /** Redirect the browser to the hosted sign-in page. */
77
+ signIn(publicKey?: string, redirectUrl?: string): void;
78
+ /** Redirect the browser to the hosted sign-up page (`prompt=signup`). */
79
+ signUp(publicKey?: string, redirectUrl?: string): void;
80
+ /** Clear the session locally and redirect to the logout endpoint. */
81
+ signOut(): void;
82
+ /**
83
+ * Fetches the current user from the identity host's userinfo endpoint and
84
+ * stores it on the `user` signal. Returns `null` when there is no token.
85
+ */
86
+ fetchUser(publicKey?: string): Promise<unknown>;
87
+ }
88
+
89
+ /**
90
+ * Functional HTTP interceptor that attaches `Authorization: Bearer <token>`
91
+ * to outgoing requests when a session token is present. Register it with
92
+ * `provideAuthdog()` or directly via `provideHttpClient(withInterceptors([authdogInterceptor]))`.
93
+ *
94
+ * Requests that already carry an `Authorization` header are left untouched.
95
+ */
96
+ declare const authdogInterceptor: HttpInterceptorFn;
97
+
98
+ /**
99
+ * ⚠️ PRESENTATIONAL / UX ONLY — NOT A SECURITY BOUNDARY.
100
+ *
101
+ * This guard prevents an unauthenticated user from *navigating* to a route in
102
+ * the SPA so they see a login redirect instead of a broken page. It runs
103
+ * entirely in the browser and is therefore trivially bypassable by any client
104
+ * (devtools, crafted requests, a patched bundle). It MUST NOT be relied on to
105
+ * protect data or actions.
106
+ *
107
+ * Every protected operation MUST be independently enforced server-side: the
108
+ * API behind the route has to validate the session on each request regardless
109
+ * of what this guard decides.
110
+ */
111
+ declare const authdogGuard: CanActivateFn;
112
+
113
+ /** Shared localStorage key for the persisted token. */
114
+ declare const TOKEN_STORAGE_KEY = "token";
115
+ /** JWT shape: three base64url segments separated by dots. */
116
+ declare const JWT_PATTERN: RegExp;
117
+ declare const getTokenFromUri: (url: string) => string | null;
118
+ interface IFetchUserData {
119
+ user: {
120
+ id: string;
121
+ environmentId: string;
122
+ externalId: string;
123
+ userName: string;
124
+ displayName: string;
125
+ nickName: string;
126
+ profileUrl: string;
127
+ title: string;
128
+ userType: string;
129
+ preferredLanguage: string | null;
130
+ locale: string | null;
131
+ timezone: string | null;
132
+ active: boolean;
133
+ provider: string;
134
+ lastLogin: string;
135
+ createdAt: string;
136
+ updatedAt: string;
137
+ names: {
138
+ id: string;
139
+ userId: string;
140
+ formatted: string | null;
141
+ familyName: string;
142
+ givenName: string;
143
+ middleName: string | null;
144
+ honorificPrefix: string | null;
145
+ honorificSuffix: string | null;
146
+ createdAt: string;
147
+ updatedAt: string;
148
+ };
149
+ addresses: [];
150
+ emails: {
151
+ value: string;
152
+ primary: boolean;
153
+ type: string;
154
+ }[];
155
+ phoneNumbers: [];
156
+ ims: [];
157
+ photos: {
158
+ value: string;
159
+ type: string;
160
+ }[];
161
+ };
162
+ meta: {
163
+ code: number;
164
+ message: string;
165
+ };
166
+ }
167
+ /**
168
+ * Fetches user data from the identity host's OIDC `userinfo` endpoint. The
169
+ * identity host is decoded through the hardened public-key parser, which
170
+ * enforces the trusted-host allowlist before the bearer token is ever sent.
171
+ */
172
+ declare const fetchUserData: (publicKey: string, token: string) => Promise<IFetchUserData | null>;
173
+ /**
174
+ * Builds the OIDC authorize URL for a sign-in or sign-up redirect. The public
175
+ * key is decoded through the hardened parser (no raw base64/JSON) so an
176
+ * untrusted identity host can never be used as the redirect target.
177
+ */
178
+ declare const buildAuthorizeUrl: (publicKey: string, redirectUri: string, options?: {
179
+ signup?: boolean;
180
+ }) => string;
181
+
182
+ /**
183
+ * Decodes and validates an Authdog public key. Delegates to the hardened
184
+ * shared parser in @authdog/node-commons, which validates the payload and
185
+ * enforces a trusted identity-host allowlist (SSRF / token-exfiltration
186
+ * protection) rather than blindly decoding base64/JSON.
187
+ */
188
+ declare const getPublicKeyPayload: (publicKey: string) => PublicKeyPayload;
189
+ /** Lightweight, fast-fail shape check before the full decode. */
190
+ declare const validatePublicKey: (publicKey: string) => void;
191
+
192
+ export { AUTHDOG_CONFIG, type AuthdogConfig, AuthdogService, type IFetchUserData, JWT_PATTERN, TOKEN_STORAGE_KEY, authdogGuard, authdogInterceptor, buildAuthorizeUrl, fetchUserData, getPublicKeyPayload, getTokenFromUri, provideAuthdog, validatePublicKey };
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";var ot=Object.create;var f=Object.defineProperty;var H=Object.getOwnPropertyDescriptor;var nt=Object.getOwnPropertyNames;var it=Object.prototype.hasOwnProperty;var L=(t,r)=>(r=Symbol[t])?r:Symbol.for("Symbol."+t),v=t=>{throw TypeError(t)};var st=(t,r,e)=>r in t?f(t,r,{enumerable:!0,configurable:!0,writable:!0,value:e}):t[r]=e;var R=(t,r)=>f(t,"name",{value:r,configurable:!0});var at=(t,r)=>{for(var e in r)f(t,e,{get:r[e],enumerable:!0})},lt=(t,r,e,n)=>{if(r&&typeof r=="object"||typeof r=="function")for(let o of nt(r))!it.call(t,o)&&o!==e&&f(t,o,{get:()=>r[o],enumerable:!(n=H(r,o))||n.enumerable});return t};var ut=t=>lt(f({},"__esModule",{value:!0}),t);var j=t=>[,,,ot(t?.[L("metadata")]??null)],$=["class","method","getter","setter","accessor","field","value","get","set"],_=t=>t!==void 0&&typeof t!="function"?v("Function expected"):t,ct=(t,r,e,n,o)=>({kind:$[t],name:r,metadata:n,addInitializer:s=>e._?v("Already initialized"):o.push(_(s||null))}),dt=(t,r)=>st(r,L("metadata"),t[3]),W=(t,r,e,n)=>{for(var o=0,s=t[r>>1],p=s&&s.length;o<p;o++)r&1?s[o].call(e):n=s[o].call(e,n);return n},B=(t,r,e,n,o,s)=>{var p,u,G,y,U,i=r&7,x=!!(r&8),g=!!(r&16),E=i>3?t.length+1:i?x?1:2:0,S=$[i+5],D=i>3&&(t[E-1]=[]),et=t[E]||(t[E]=[]),c=i&&(!g&&!x&&(o=o.prototype),i<5&&(i>3||!g)&&H(i<4?o:{get[e](){return C(this,s)},set[e](a){return z(this,s,a)}},e));i?g&&i<4&&R(s,(i>2?"set ":i>1?"get ":"")+e):R(o,e);for(var O=n.length-1;O>=0;O--)y=ct(i,e,G={},t[3],et),i&&(y.static=x,y.private=g,U=y.access={has:g?a=>gt(o,a):a=>e in a},i^3&&(U.get=g?a=>(i^1?C:pt)(a,o,i^4?s:c.get):a=>a[e]),i>2&&(U.set=g?(a,K)=>z(a,o,K,i^4?s:c.set):(a,K)=>a[e]=K)),u=(0,n[O])(i?i<4?g?s:c[S]:i>4?void 0:{get:c.get,set:c.set}:o,y),G._=1,i^4||u===void 0?_(u)&&(i>4?D.unshift(u):i?g?s=u:c[S]=u:o=u):typeof u!="object"||u===null?v("Object expected"):(_(p=u.get)&&(c.get=p),_(p=u.set)&&(c.set=p),_(p=u.init)&&D.unshift(p));return i||dt(t,o),c&&f(o,e,c),g?i^4?s:c:o};var N=(t,r,e)=>r.has(t)||v("Cannot "+e),gt=(t,r)=>Object(r)!==r?v('Cannot use the "in" operator on this value'):t.has(r),C=(t,r,e)=>(N(t,r,"read from private field"),e?e.call(t):r.get(t));var z=(t,r,e,n)=>(N(t,r,"write to private field"),n?n.call(t,e):r.set(t,e),e),pt=(t,r,e)=>(N(t,r,"access private method"),e);var ht={};at(ht,{AUTHDOG_CONFIG:()=>h,AuthdogService:()=>l,JWT_PATTERN:()=>T,TOKEN_STORAGE_KEY:()=>m,authdogGuard:()=>rt,authdogInterceptor:()=>q,buildAuthorizeUrl:()=>b,fetchUserData:()=>k,getPublicKeyPayload:()=>P,getTokenFromUri:()=>Z,provideAuthdog:()=>Q,validatePublicKey:()=>A});module.exports=ut(ht);var M=require("@angular/core");var J=require("@angular/core"),h=new J.InjectionToken("AUTHDOG_CONFIG");var d=require("@angular/core");var Y=require("@authdog/node-commons"),P=t=>(0,Y.validateAndParsePublicKey)(t),A=t=>{if(!t)throw new Error("Public key is not defined");if(!t.startsWith("pk_"))throw new Error("Invalid public key")};var m="token",T=/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/,Z=t=>new URL(t).searchParams.get("token"),k=async(t,r)=>{A(t);let e=P(t),n=await fetch(`${e.identityHost}/oidc/${e.environmentId}/userinfo`,{headers:{authorization:`Bearer ${r}`}});if(!n.ok)throw new Error("Failed to fetch user info");return await n.json()},b=(t,r,e={})=>{A(t);let n=P(t),o=new URL(`${n.identityHost}/oidc/${n.environmentId}/authorize`);return o.searchParams.set("client_id",t),o.searchParams.set("response_type","code"),o.searchParams.set("scope","openid profile email"),o.searchParams.set("redirect_uri",r),e.signup&&o.searchParams.set("prompt","signup"),o.toString()};var w=()=>typeof window<"u",V,F;V=[(0,d.Injectable)({providedIn:"root"})];var l=class{config=(0,d.inject)(h);_token=(0,d.signal)(null);_isLoading=(0,d.signal)(!0);_user=(0,d.signal)(null);_error=(0,d.signal)(null);token=this._token.asReadonly();isLoading=this._isLoading.asReadonly();user=this._user.asReadonly();error=this._error.asReadonly();isAuthenticated=(0,d.computed)(()=>!!this._token()&&!!this._user());constructor(){this.bootstrap()}bootstrap(){if(!w()){this._isLoading.set(!1);return}let r=new URL(window.location.href),e=r.searchParams.get("token");if(e&&(r.searchParams.delete("token"),window.history.replaceState({},document.title,r.toString()),T.test(e))){localStorage.setItem(m,e),this._token.set(e),this._isLoading.set(!1);return}let n=localStorage.getItem(m);n&&this._token.set(n),this._isLoading.set(!1)}setToken(r){this._token.set(r),w()&&(r?localStorage.setItem(m,r):localStorage.removeItem(m))}signIn(r=this.config.publicKey,e){if(w()){this._error.set(null);try{window.location.href=b(r,e||window.location.origin)}catch(n){this._error.set(n)}}}signUp(r=this.config.publicKey,e){if(w()){this._error.set(null);try{window.location.href=b(r,e||window.location.origin,{signup:!0})}catch(n){this._error.set(n)}}}signOut(){this.setToken(null),this._user.set(null),w()&&(window.location.href="/logout")}async fetchUser(r=this.config.publicKey){let e=this._token();if(!e)return null;this._error.set(null);try{let o=(await k(r,e))?.user??null;return this._user.set(o),o}catch(n){return this._error.set(n),null}}};F=j(null),l=B(F,0,"AuthdogService",V,l),W(F,1,l);var Q=t=>(0,M.makeEnvironmentProviders)([{provide:h,useValue:t},l]);var X=require("@angular/core");var q=(t,r)=>{let n=(0,X.inject)(l).token();return!n||t.headers.has("Authorization")?r(t):r(t.clone({setHeaders:{Authorization:`Bearer ${n}`}}))};var I=require("@angular/core"),tt=require("@angular/router");var rt=()=>{let t=(0,I.inject)(l),r=(0,I.inject)(tt.Router),e=(0,I.inject)(h);return t.token()?!0:r.parseUrl(e.loginPath??"/")};0&&(module.exports={AUTHDOG_CONFIG,AuthdogService,JWT_PATTERN,TOKEN_STORAGE_KEY,authdogGuard,authdogInterceptor,buildAuthorizeUrl,fetchUserData,getPublicKeyPayload,getTokenFromUri,provideAuthdog,validatePublicKey});
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/provider.ts","../src/tokens.ts","../src/service.ts","../src/commons.ts","../src/session.ts","../src/interceptor.ts","../src/guard.ts"],"sourcesContent":["export { provideAuthdog } from \"./provider\";\nexport { AuthdogService } from \"./service\";\nexport { authdogInterceptor } from \"./interceptor\";\nexport { authdogGuard } from \"./guard\";\nexport { AUTHDOG_CONFIG, type AuthdogConfig } from \"./tokens\";\nexport {\n fetchUserData,\n buildAuthorizeUrl,\n getTokenFromUri,\n TOKEN_STORAGE_KEY,\n JWT_PATTERN,\n type IFetchUserData,\n} from \"./session\";\nexport {\n getPublicKeyPayload,\n validatePublicKey,\n type PublicKeyPayload,\n} from \"./commons\";\n","import {\n type EnvironmentProviders,\n type Provider,\n makeEnvironmentProviders,\n} from \"@angular/core\";\nimport { AUTHDOG_CONFIG, type AuthdogConfig } from \"./tokens\";\nimport { AuthdogService } from \"./service\";\n\n/**\n * Wires the Authdog SDK into a standalone Angular application.\n *\n * Add to `ApplicationConfig.providers`:\n *\n * ```ts\n * export const appConfig: ApplicationConfig = {\n * providers: [\n * provideAuthdog({ publicKey: environment.authdogPublicKey }),\n * provideHttpClient(withInterceptors([authdogInterceptor])),\n * ],\n * };\n * ```\n *\n * Note: the HTTP interceptor is registered by the consumer via\n * `withInterceptors([authdogInterceptor])` so it composes with the app's own\n * `provideHttpClient` setup rather than forcing a particular configuration.\n */\nexport const provideAuthdog = (\n config: AuthdogConfig,\n): EnvironmentProviders => {\n const providers: Provider[] = [\n { provide: AUTHDOG_CONFIG, useValue: config },\n AuthdogService,\n ];\n\n return makeEnvironmentProviders(providers);\n};\n","import { InjectionToken } from \"@angular/core\";\n\nexport interface AuthdogConfig {\n /**\n * Authdog public key (`pk_…`). Safe to expose to the browser. Used as the\n * `client_id` and to derive the identity host / environment for OIDC flows.\n */\n publicKey: string;\n\n /**\n * Path the route guard redirects to when the user is not authenticated.\n * Defaults to `/`.\n */\n loginPath?: string;\n}\n\n/** DI token carrying the SDK configuration provided via `provideAuthdog`. */\nexport const AUTHDOG_CONFIG = new InjectionToken<AuthdogConfig>(\n \"AUTHDOG_CONFIG\",\n);\n","import {\n Injectable,\n computed,\n inject,\n signal,\n type Signal,\n} from \"@angular/core\";\nimport { AUTHDOG_CONFIG } from \"./tokens\";\nimport {\n JWT_PATTERN,\n TOKEN_STORAGE_KEY,\n buildAuthorizeUrl,\n fetchUserData,\n} from \"./session\";\n\nconst isBrowser = (): boolean => typeof window !== \"undefined\";\n\n/**\n * Root-provided service that owns the client-side auth state.\n *\n * State is exposed as readonly signals so templates and `computed`s react to\n * changes automatically. The service is browser-first: every `window` /\n * `localStorage` access is guarded so it is inert under Angular Universal\n * (SSR), where it simply reports \"not loading, no token\".\n */\n@Injectable({ providedIn: \"root\" })\nexport class AuthdogService {\n private readonly config = inject(AUTHDOG_CONFIG);\n\n private readonly _token = signal<string | null>(null);\n private readonly _isLoading = signal<boolean>(true);\n private readonly _user = signal<unknown>(null);\n private readonly _error = signal<Error | null>(null);\n\n /** Current bearer token, or `null` when signed out. */\n readonly token: Signal<string | null> = this._token.asReadonly();\n /** True until the initial token-from-URL / localStorage bootstrap finishes. */\n readonly isLoading: Signal<boolean> = this._isLoading.asReadonly();\n /** The last fetched userinfo `user` payload, or `null`. */\n readonly user: Signal<unknown> = this._user.asReadonly();\n /** The last error raised by a sign-in / fetch operation, or `null`. */\n readonly error: Signal<Error | null> = this._error.asReadonly();\n\n /** True when a token is present AND a user has been loaded. */\n readonly isAuthenticated: Signal<boolean> = computed(\n () => !!this._token() && !!this._user(),\n );\n\n constructor() {\n this.bootstrap();\n }\n\n /**\n * Reads a token from the URL (`?token=`) or localStorage. A URL token is\n * validated against the JWT pattern BEFORE it is persisted, so arbitrary\n * attacker-supplied query data is never written to storage, and is stripped\n * from the address bar via `history.replaceState` regardless of validity.\n */\n private bootstrap(): void {\n if (!isBrowser()) {\n this._isLoading.set(false);\n return;\n }\n\n const url = new URL(window.location.href);\n const urlToken = url.searchParams.get(\"token\");\n\n if (urlToken) {\n // Remove the token from the URL without a reload, valid or not.\n url.searchParams.delete(\"token\");\n window.history.replaceState({}, document.title, url.toString());\n\n // Only persist values that look like a JWT.\n if (JWT_PATTERN.test(urlToken)) {\n localStorage.setItem(TOKEN_STORAGE_KEY, urlToken);\n this._token.set(urlToken);\n this._isLoading.set(false);\n return;\n }\n }\n\n const existingToken = localStorage.getItem(TOKEN_STORAGE_KEY);\n if (existingToken) {\n this._token.set(existingToken);\n }\n\n this._isLoading.set(false);\n }\n\n /** Imperatively set (or clear) the in-memory + persisted token. */\n setToken(token: string | null): void {\n this._token.set(token);\n if (!isBrowser()) return;\n\n if (token) {\n localStorage.setItem(TOKEN_STORAGE_KEY, token);\n } else {\n localStorage.removeItem(TOKEN_STORAGE_KEY);\n }\n }\n\n /** Redirect the browser to the hosted sign-in page. */\n signIn(publicKey: string = this.config.publicKey, redirectUrl?: string): void {\n if (!isBrowser()) return;\n this._error.set(null);\n try {\n window.location.href = buildAuthorizeUrl(\n publicKey,\n redirectUrl || window.location.origin,\n );\n } catch (err) {\n this._error.set(err as Error);\n }\n }\n\n /** Redirect the browser to the hosted sign-up page (`prompt=signup`). */\n signUp(publicKey: string = this.config.publicKey, redirectUrl?: string): void {\n if (!isBrowser()) return;\n this._error.set(null);\n try {\n window.location.href = buildAuthorizeUrl(\n publicKey,\n redirectUrl || window.location.origin,\n { signup: true },\n );\n } catch (err) {\n this._error.set(err as Error);\n }\n }\n\n /** Clear the session locally and redirect to the logout endpoint. */\n signOut(): void {\n this.setToken(null);\n this._user.set(null);\n if (isBrowser()) {\n window.location.href = \"/logout\";\n }\n }\n\n /**\n * Fetches the current user from the identity host's userinfo endpoint and\n * stores it on the `user` signal. Returns `null` when there is no token.\n */\n async fetchUser(\n publicKey: string = this.config.publicKey,\n ): Promise<unknown> {\n const token = this._token();\n if (!token) {\n return null;\n }\n\n this._error.set(null);\n try {\n const userData = await fetchUserData(publicKey, token);\n const user = userData?.user ?? null;\n this._user.set(user);\n return user;\n } catch (err) {\n this._error.set(err as Error);\n return null;\n }\n }\n}\n","import {\n validateAndParsePublicKey,\n type PublicKeyPayload,\n} from \"@authdog/node-commons\";\n\nexport type { PublicKeyPayload };\n\n/**\n * Decodes and validates an Authdog public key. Delegates to the hardened\n * shared parser in @authdog/node-commons, which validates the payload and\n * enforces a trusted identity-host allowlist (SSRF / token-exfiltration\n * protection) rather than blindly decoding base64/JSON.\n */\nexport const getPublicKeyPayload = (publicKey: string): PublicKeyPayload => {\n return validateAndParsePublicKey(publicKey);\n};\n\n/** Lightweight, fast-fail shape check before the full decode. */\nexport const validatePublicKey = (publicKey: string): void => {\n if (!publicKey) {\n throw new Error(\"Public key is not defined\");\n }\n\n if (!publicKey.startsWith(\"pk_\")) {\n throw new Error(\"Invalid public key\");\n }\n};\n","import { getPublicKeyPayload, validatePublicKey } from \"./commons\";\n\n/** Shared localStorage key for the persisted token. */\nexport const TOKEN_STORAGE_KEY = \"token\";\n\n/** JWT shape: three base64url segments separated by dots. */\nexport const JWT_PATTERN = /^[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+$/;\n\nexport const getTokenFromUri = (url: string): string | null => {\n return new URL(url).searchParams.get(\"token\");\n};\n\nexport interface IFetchUserData {\n user: {\n id: string;\n environmentId: string;\n externalId: string;\n userName: string;\n displayName: string;\n nickName: string;\n profileUrl: string;\n title: string;\n userType: string;\n preferredLanguage: string | null;\n locale: string | null;\n timezone: string | null;\n active: boolean;\n provider: string;\n lastLogin: string;\n createdAt: string;\n updatedAt: string;\n names: {\n id: string;\n userId: string;\n formatted: string | null;\n familyName: string;\n givenName: string;\n middleName: string | null;\n honorificPrefix: string | null;\n honorificSuffix: string | null;\n createdAt: string;\n updatedAt: string;\n };\n addresses: [];\n emails: {\n value: string;\n primary: boolean;\n type: string;\n }[];\n phoneNumbers: [];\n ims: [];\n photos: {\n value: string;\n type: string;\n }[];\n };\n meta: {\n code: number;\n message: string;\n };\n}\n\n/**\n * Fetches user data from the identity host's OIDC `userinfo` endpoint. The\n * identity host is decoded through the hardened public-key parser, which\n * enforces the trusted-host allowlist before the bearer token is ever sent.\n */\nexport const fetchUserData = async (\n publicKey: string,\n token: string,\n): Promise<IFetchUserData | null> => {\n validatePublicKey(publicKey);\n const publicKeyObj = getPublicKeyPayload(publicKey);\n\n const userData = await fetch(\n `${publicKeyObj.identityHost}/oidc/${publicKeyObj.environmentId}/userinfo`,\n {\n headers: {\n authorization: `Bearer ${token}`,\n },\n },\n );\n\n if (!userData.ok) {\n throw new Error(\"Failed to fetch user info\");\n }\n\n return (await userData.json()) as IFetchUserData;\n};\n\n/**\n * Builds the OIDC authorize URL for a sign-in or sign-up redirect. The public\n * key is decoded through the hardened parser (no raw base64/JSON) so an\n * untrusted identity host can never be used as the redirect target.\n */\nexport const buildAuthorizeUrl = (\n publicKey: string,\n redirectUri: string,\n options: { signup?: boolean } = {},\n): string => {\n validatePublicKey(publicKey);\n const publicKeyObj = getPublicKeyPayload(publicKey);\n\n const authUrl = new URL(\n `${publicKeyObj.identityHost}/oidc/${publicKeyObj.environmentId}/authorize`,\n );\n authUrl.searchParams.set(\"client_id\", publicKey);\n authUrl.searchParams.set(\"response_type\", \"code\");\n authUrl.searchParams.set(\"scope\", \"openid profile email\");\n authUrl.searchParams.set(\"redirect_uri\", redirectUri);\n\n if (options.signup) {\n authUrl.searchParams.set(\"prompt\", \"signup\");\n }\n\n return authUrl.toString();\n};\n","import { inject } from \"@angular/core\";\nimport type { HttpInterceptorFn } from \"@angular/common/http\";\nimport { AuthdogService } from \"./service\";\n\n/**\n * Functional HTTP interceptor that attaches `Authorization: Bearer <token>`\n * to outgoing requests when a session token is present. Register it with\n * `provideAuthdog()` or directly via `provideHttpClient(withInterceptors([authdogInterceptor]))`.\n *\n * Requests that already carry an `Authorization` header are left untouched.\n */\nexport const authdogInterceptor: HttpInterceptorFn = (req, next) => {\n const auth = inject(AuthdogService);\n const token = auth.token();\n\n if (!token || req.headers.has(\"Authorization\")) {\n return next(req);\n }\n\n return next(\n req.clone({\n setHeaders: { Authorization: `Bearer ${token}` },\n }),\n );\n};\n","import { inject } from \"@angular/core\";\nimport { Router, type CanActivateFn, type UrlTree } from \"@angular/router\";\nimport { AuthdogService } from \"./service\";\nimport { AUTHDOG_CONFIG } from \"./tokens\";\n\n/**\n * ⚠️ PRESENTATIONAL / UX ONLY — NOT A SECURITY BOUNDARY.\n *\n * This guard prevents an unauthenticated user from *navigating* to a route in\n * the SPA so they see a login redirect instead of a broken page. It runs\n * entirely in the browser and is therefore trivially bypassable by any client\n * (devtools, crafted requests, a patched bundle). It MUST NOT be relied on to\n * protect data or actions.\n *\n * Every protected operation MUST be independently enforced server-side: the\n * API behind the route has to validate the session on each request regardless\n * of what this guard decides.\n */\nexport const authdogGuard: CanActivateFn = (): boolean | UrlTree => {\n const auth = inject(AuthdogService);\n const router = inject(Router);\n const config = inject(AUTHDOG_CONFIG);\n\n // A token is sufficient for *navigation* — fetching the user is async and\n // we don't want to block routing on a network round-trip.\n if (auth.token()) {\n return true;\n }\n\n return router.parseUrl(config.loginPath ?? \"/\");\n};\n"],"mappings":"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","names":["index_exports","__export","AUTHDOG_CONFIG","AuthdogService","JWT_PATTERN","TOKEN_STORAGE_KEY","authdogGuard","authdogInterceptor","buildAuthorizeUrl","fetchUserData","getPublicKeyPayload","getTokenFromUri","provideAuthdog","validatePublicKey","__toCommonJS","import_core","import_core","AUTHDOG_CONFIG","import_core","import_node_commons","getPublicKeyPayload","publicKey","validatePublicKey","TOKEN_STORAGE_KEY","JWT_PATTERN","getTokenFromUri","url","fetchUserData","publicKey","token","validatePublicKey","publicKeyObj","getPublicKeyPayload","userData","buildAuthorizeUrl","redirectUri","options","authUrl","isBrowser","_AuthdogService_decorators","_init","AuthdogService","AUTHDOG_CONFIG","url","urlToken","JWT_PATTERN","TOKEN_STORAGE_KEY","existingToken","token","publicKey","redirectUrl","buildAuthorizeUrl","err","user","fetchUserData","__decoratorStart","__decorateElement","__runInitializers","provideAuthdog","config","AUTHDOG_CONFIG","AuthdogService","import_core","authdogInterceptor","req","next","token","AuthdogService","import_core","import_router","authdogGuard","auth","AuthdogService","router","config","AUTHDOG_CONFIG"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,2 @@
1
+ var Y=Object.create;var x=Object.defineProperty;var Z=Object.getOwnPropertyDescriptor;var H=(t,r)=>(r=Symbol[t])?r:Symbol.for("Symbol."+t),y=t=>{throw TypeError(t)};var V=(t,r,e)=>r in t?x(t,r,{enumerable:!0,configurable:!0,writable:!0,value:e}):t[r]=e;var R=(t,r)=>x(t,"name",{value:r,configurable:!0});var L=t=>[,,,Y(t?.[H("metadata")]??null)],j=["class","method","getter","setter","accessor","field","value","get","set"],f=t=>t!==void 0&&typeof t!="function"?y("Function expected"):t,M=(t,r,e,o,n)=>({kind:j[t],name:r,metadata:o,addInitializer:s=>e._?y("Already initialized"):n.push(f(s||null))}),Q=(t,r)=>V(r,H("metadata"),t[3]),$=(t,r,e,o)=>{for(var n=0,s=t[r>>1],g=s&&s.length;n<g;n++)r&1?s[n].call(e):o=s[n].call(e,o);return o},W=(t,r,e,o,n,s)=>{var g,l,G,m,w,i=r&7,T=!!(r&8),d=!!(r&16),k=i>3?t.length+1:i?T?1:2:0,S=j[i+5],D=i>3&&(t[k-1]=[]),J=t[k]||(t[k]=[]),c=i&&(!d&&!T&&(n=n.prototype),i<5&&(i>3||!d)&&Z(i<4?n:{get[e](){return C(this,s)},set[e](a){return z(this,s,a)}},e));i?d&&i<4&&R(s,(i>2?"set ":i>1?"get ":"")+e):R(n,e);for(var I=o.length-1;I>=0;I--)m=M(i,e,G={},t[3],J),i&&(m.static=T,m.private=d,w=m.access={has:d?a=>X(n,a):a=>e in a},i^3&&(w.get=d?a=>(i^1?C:q)(a,n,i^4?s:c.get):a=>a[e]),i>2&&(w.set=d?(a,U)=>z(a,n,U,i^4?s:c.set):(a,U)=>a[e]=U)),l=(0,o[I])(i?i<4?d?s:c[S]:i>4?void 0:{get:c.get,set:c.set}:n,m),G._=1,i^4||l===void 0?f(l)&&(i>4?D.unshift(l):i?d?s=l:c[S]=l:n=l):typeof l!="object"||l===null?y("Object expected"):(f(g=l.get)&&(c.get=g),f(g=l.set)&&(c.set=g),f(g=l.init)&&D.unshift(g));return i||Q(t,n),c&&x(n,e,c),d?i^4?s:c:n};var E=(t,r,e)=>r.has(t)||y("Cannot "+e),X=(t,r)=>Object(r)!==r?y('Cannot use the "in" operator on this value'):t.has(r),C=(t,r,e)=>(E(t,r,"read from private field"),e?e.call(t):r.get(t));var z=(t,r,e,o)=>(E(t,r,"write to private field"),o?o.call(t,e):r.set(t,e),e),q=(t,r,e)=>(E(t,r,"access private method"),e);import{makeEnvironmentProviders as st}from"@angular/core";import{InjectionToken as tt}from"@angular/core";var p=new tt("AUTHDOG_CONFIG");import{Injectable as ot,computed as nt,inject as it,signal as b}from"@angular/core";import{validateAndParsePublicKey as rt}from"@authdog/node-commons";var v=t=>rt(t),P=t=>{if(!t)throw new Error("Public key is not defined");if(!t.startsWith("pk_"))throw new Error("Invalid public key")};var h="token",O=/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/,et=t=>new URL(t).searchParams.get("token"),K=async(t,r)=>{P(t);let e=v(t),o=await fetch(`${e.identityHost}/oidc/${e.environmentId}/userinfo`,{headers:{authorization:`Bearer ${r}`}});if(!o.ok)throw new Error("Failed to fetch user info");return await o.json()},A=(t,r,e={})=>{P(t);let o=v(t),n=new URL(`${o.identityHost}/oidc/${o.environmentId}/authorize`);return n.searchParams.set("client_id",t),n.searchParams.set("response_type","code"),n.searchParams.set("scope","openid profile email"),n.searchParams.set("redirect_uri",r),e.signup&&n.searchParams.set("prompt","signup"),n.toString()};var _=()=>typeof window<"u",B,N;B=[ot({providedIn:"root"})];var u=class{config=it(p);_token=b(null);_isLoading=b(!0);_user=b(null);_error=b(null);token=this._token.asReadonly();isLoading=this._isLoading.asReadonly();user=this._user.asReadonly();error=this._error.asReadonly();isAuthenticated=nt(()=>!!this._token()&&!!this._user());constructor(){this.bootstrap()}bootstrap(){if(!_()){this._isLoading.set(!1);return}let r=new URL(window.location.href),e=r.searchParams.get("token");if(e&&(r.searchParams.delete("token"),window.history.replaceState({},document.title,r.toString()),O.test(e))){localStorage.setItem(h,e),this._token.set(e),this._isLoading.set(!1);return}let o=localStorage.getItem(h);o&&this._token.set(o),this._isLoading.set(!1)}setToken(r){this._token.set(r),_()&&(r?localStorage.setItem(h,r):localStorage.removeItem(h))}signIn(r=this.config.publicKey,e){if(_()){this._error.set(null);try{window.location.href=A(r,e||window.location.origin)}catch(o){this._error.set(o)}}}signUp(r=this.config.publicKey,e){if(_()){this._error.set(null);try{window.location.href=A(r,e||window.location.origin,{signup:!0})}catch(o){this._error.set(o)}}}signOut(){this.setToken(null),this._user.set(null),_()&&(window.location.href="/logout")}async fetchUser(r=this.config.publicKey){let e=this._token();if(!e)return null;this._error.set(null);try{let n=(await K(r,e))?.user??null;return this._user.set(n),n}catch(o){return this._error.set(o),null}}};N=L(null),u=W(N,0,"AuthdogService",B,u),$(N,1,u);var at=t=>st([{provide:p,useValue:t},u]);import{inject as lt}from"@angular/core";var ut=(t,r)=>{let o=lt(u).token();return!o||t.headers.has("Authorization")?r(t):r(t.clone({setHeaders:{Authorization:`Bearer ${o}`}}))};import{inject as F}from"@angular/core";import{Router as ct}from"@angular/router";var dt=()=>{let t=F(u),r=F(ct),e=F(p);return t.token()?!0:r.parseUrl(e.loginPath??"/")};export{p as AUTHDOG_CONFIG,u as AuthdogService,O as JWT_PATTERN,h as TOKEN_STORAGE_KEY,dt as authdogGuard,ut as authdogInterceptor,A as buildAuthorizeUrl,K as fetchUserData,v as getPublicKeyPayload,et as getTokenFromUri,at as provideAuthdog,P as validatePublicKey};
2
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/provider.ts","../src/tokens.ts","../src/service.ts","../src/commons.ts","../src/session.ts","../src/interceptor.ts","../src/guard.ts"],"sourcesContent":["import {\n type EnvironmentProviders,\n type Provider,\n makeEnvironmentProviders,\n} from \"@angular/core\";\nimport { AUTHDOG_CONFIG, type AuthdogConfig } from \"./tokens\";\nimport { AuthdogService } from \"./service\";\n\n/**\n * Wires the Authdog SDK into a standalone Angular application.\n *\n * Add to `ApplicationConfig.providers`:\n *\n * ```ts\n * export const appConfig: ApplicationConfig = {\n * providers: [\n * provideAuthdog({ publicKey: environment.authdogPublicKey }),\n * provideHttpClient(withInterceptors([authdogInterceptor])),\n * ],\n * };\n * ```\n *\n * Note: the HTTP interceptor is registered by the consumer via\n * `withInterceptors([authdogInterceptor])` so it composes with the app's own\n * `provideHttpClient` setup rather than forcing a particular configuration.\n */\nexport const provideAuthdog = (\n config: AuthdogConfig,\n): EnvironmentProviders => {\n const providers: Provider[] = [\n { provide: AUTHDOG_CONFIG, useValue: config },\n AuthdogService,\n ];\n\n return makeEnvironmentProviders(providers);\n};\n","import { InjectionToken } from \"@angular/core\";\n\nexport interface AuthdogConfig {\n /**\n * Authdog public key (`pk_…`). Safe to expose to the browser. Used as the\n * `client_id` and to derive the identity host / environment for OIDC flows.\n */\n publicKey: string;\n\n /**\n * Path the route guard redirects to when the user is not authenticated.\n * Defaults to `/`.\n */\n loginPath?: string;\n}\n\n/** DI token carrying the SDK configuration provided via `provideAuthdog`. */\nexport const AUTHDOG_CONFIG = new InjectionToken<AuthdogConfig>(\n \"AUTHDOG_CONFIG\",\n);\n","import {\n Injectable,\n computed,\n inject,\n signal,\n type Signal,\n} from \"@angular/core\";\nimport { AUTHDOG_CONFIG } from \"./tokens\";\nimport {\n JWT_PATTERN,\n TOKEN_STORAGE_KEY,\n buildAuthorizeUrl,\n fetchUserData,\n} from \"./session\";\n\nconst isBrowser = (): boolean => typeof window !== \"undefined\";\n\n/**\n * Root-provided service that owns the client-side auth state.\n *\n * State is exposed as readonly signals so templates and `computed`s react to\n * changes automatically. The service is browser-first: every `window` /\n * `localStorage` access is guarded so it is inert under Angular Universal\n * (SSR), where it simply reports \"not loading, no token\".\n */\n@Injectable({ providedIn: \"root\" })\nexport class AuthdogService {\n private readonly config = inject(AUTHDOG_CONFIG);\n\n private readonly _token = signal<string | null>(null);\n private readonly _isLoading = signal<boolean>(true);\n private readonly _user = signal<unknown>(null);\n private readonly _error = signal<Error | null>(null);\n\n /** Current bearer token, or `null` when signed out. */\n readonly token: Signal<string | null> = this._token.asReadonly();\n /** True until the initial token-from-URL / localStorage bootstrap finishes. */\n readonly isLoading: Signal<boolean> = this._isLoading.asReadonly();\n /** The last fetched userinfo `user` payload, or `null`. */\n readonly user: Signal<unknown> = this._user.asReadonly();\n /** The last error raised by a sign-in / fetch operation, or `null`. */\n readonly error: Signal<Error | null> = this._error.asReadonly();\n\n /** True when a token is present AND a user has been loaded. */\n readonly isAuthenticated: Signal<boolean> = computed(\n () => !!this._token() && !!this._user(),\n );\n\n constructor() {\n this.bootstrap();\n }\n\n /**\n * Reads a token from the URL (`?token=`) or localStorage. A URL token is\n * validated against the JWT pattern BEFORE it is persisted, so arbitrary\n * attacker-supplied query data is never written to storage, and is stripped\n * from the address bar via `history.replaceState` regardless of validity.\n */\n private bootstrap(): void {\n if (!isBrowser()) {\n this._isLoading.set(false);\n return;\n }\n\n const url = new URL(window.location.href);\n const urlToken = url.searchParams.get(\"token\");\n\n if (urlToken) {\n // Remove the token from the URL without a reload, valid or not.\n url.searchParams.delete(\"token\");\n window.history.replaceState({}, document.title, url.toString());\n\n // Only persist values that look like a JWT.\n if (JWT_PATTERN.test(urlToken)) {\n localStorage.setItem(TOKEN_STORAGE_KEY, urlToken);\n this._token.set(urlToken);\n this._isLoading.set(false);\n return;\n }\n }\n\n const existingToken = localStorage.getItem(TOKEN_STORAGE_KEY);\n if (existingToken) {\n this._token.set(existingToken);\n }\n\n this._isLoading.set(false);\n }\n\n /** Imperatively set (or clear) the in-memory + persisted token. */\n setToken(token: string | null): void {\n this._token.set(token);\n if (!isBrowser()) return;\n\n if (token) {\n localStorage.setItem(TOKEN_STORAGE_KEY, token);\n } else {\n localStorage.removeItem(TOKEN_STORAGE_KEY);\n }\n }\n\n /** Redirect the browser to the hosted sign-in page. */\n signIn(publicKey: string = this.config.publicKey, redirectUrl?: string): void {\n if (!isBrowser()) return;\n this._error.set(null);\n try {\n window.location.href = buildAuthorizeUrl(\n publicKey,\n redirectUrl || window.location.origin,\n );\n } catch (err) {\n this._error.set(err as Error);\n }\n }\n\n /** Redirect the browser to the hosted sign-up page (`prompt=signup`). */\n signUp(publicKey: string = this.config.publicKey, redirectUrl?: string): void {\n if (!isBrowser()) return;\n this._error.set(null);\n try {\n window.location.href = buildAuthorizeUrl(\n publicKey,\n redirectUrl || window.location.origin,\n { signup: true },\n );\n } catch (err) {\n this._error.set(err as Error);\n }\n }\n\n /** Clear the session locally and redirect to the logout endpoint. */\n signOut(): void {\n this.setToken(null);\n this._user.set(null);\n if (isBrowser()) {\n window.location.href = \"/logout\";\n }\n }\n\n /**\n * Fetches the current user from the identity host's userinfo endpoint and\n * stores it on the `user` signal. Returns `null` when there is no token.\n */\n async fetchUser(\n publicKey: string = this.config.publicKey,\n ): Promise<unknown> {\n const token = this._token();\n if (!token) {\n return null;\n }\n\n this._error.set(null);\n try {\n const userData = await fetchUserData(publicKey, token);\n const user = userData?.user ?? null;\n this._user.set(user);\n return user;\n } catch (err) {\n this._error.set(err as Error);\n return null;\n }\n }\n}\n","import {\n validateAndParsePublicKey,\n type PublicKeyPayload,\n} from \"@authdog/node-commons\";\n\nexport type { PublicKeyPayload };\n\n/**\n * Decodes and validates an Authdog public key. Delegates to the hardened\n * shared parser in @authdog/node-commons, which validates the payload and\n * enforces a trusted identity-host allowlist (SSRF / token-exfiltration\n * protection) rather than blindly decoding base64/JSON.\n */\nexport const getPublicKeyPayload = (publicKey: string): PublicKeyPayload => {\n return validateAndParsePublicKey(publicKey);\n};\n\n/** Lightweight, fast-fail shape check before the full decode. */\nexport const validatePublicKey = (publicKey: string): void => {\n if (!publicKey) {\n throw new Error(\"Public key is not defined\");\n }\n\n if (!publicKey.startsWith(\"pk_\")) {\n throw new Error(\"Invalid public key\");\n }\n};\n","import { getPublicKeyPayload, validatePublicKey } from \"./commons\";\n\n/** Shared localStorage key for the persisted token. */\nexport const TOKEN_STORAGE_KEY = \"token\";\n\n/** JWT shape: three base64url segments separated by dots. */\nexport const JWT_PATTERN = /^[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+$/;\n\nexport const getTokenFromUri = (url: string): string | null => {\n return new URL(url).searchParams.get(\"token\");\n};\n\nexport interface IFetchUserData {\n user: {\n id: string;\n environmentId: string;\n externalId: string;\n userName: string;\n displayName: string;\n nickName: string;\n profileUrl: string;\n title: string;\n userType: string;\n preferredLanguage: string | null;\n locale: string | null;\n timezone: string | null;\n active: boolean;\n provider: string;\n lastLogin: string;\n createdAt: string;\n updatedAt: string;\n names: {\n id: string;\n userId: string;\n formatted: string | null;\n familyName: string;\n givenName: string;\n middleName: string | null;\n honorificPrefix: string | null;\n honorificSuffix: string | null;\n createdAt: string;\n updatedAt: string;\n };\n addresses: [];\n emails: {\n value: string;\n primary: boolean;\n type: string;\n }[];\n phoneNumbers: [];\n ims: [];\n photos: {\n value: string;\n type: string;\n }[];\n };\n meta: {\n code: number;\n message: string;\n };\n}\n\n/**\n * Fetches user data from the identity host's OIDC `userinfo` endpoint. The\n * identity host is decoded through the hardened public-key parser, which\n * enforces the trusted-host allowlist before the bearer token is ever sent.\n */\nexport const fetchUserData = async (\n publicKey: string,\n token: string,\n): Promise<IFetchUserData | null> => {\n validatePublicKey(publicKey);\n const publicKeyObj = getPublicKeyPayload(publicKey);\n\n const userData = await fetch(\n `${publicKeyObj.identityHost}/oidc/${publicKeyObj.environmentId}/userinfo`,\n {\n headers: {\n authorization: `Bearer ${token}`,\n },\n },\n );\n\n if (!userData.ok) {\n throw new Error(\"Failed to fetch user info\");\n }\n\n return (await userData.json()) as IFetchUserData;\n};\n\n/**\n * Builds the OIDC authorize URL for a sign-in or sign-up redirect. The public\n * key is decoded through the hardened parser (no raw base64/JSON) so an\n * untrusted identity host can never be used as the redirect target.\n */\nexport const buildAuthorizeUrl = (\n publicKey: string,\n redirectUri: string,\n options: { signup?: boolean } = {},\n): string => {\n validatePublicKey(publicKey);\n const publicKeyObj = getPublicKeyPayload(publicKey);\n\n const authUrl = new URL(\n `${publicKeyObj.identityHost}/oidc/${publicKeyObj.environmentId}/authorize`,\n );\n authUrl.searchParams.set(\"client_id\", publicKey);\n authUrl.searchParams.set(\"response_type\", \"code\");\n authUrl.searchParams.set(\"scope\", \"openid profile email\");\n authUrl.searchParams.set(\"redirect_uri\", redirectUri);\n\n if (options.signup) {\n authUrl.searchParams.set(\"prompt\", \"signup\");\n }\n\n return authUrl.toString();\n};\n","import { inject } from \"@angular/core\";\nimport type { HttpInterceptorFn } from \"@angular/common/http\";\nimport { AuthdogService } from \"./service\";\n\n/**\n * Functional HTTP interceptor that attaches `Authorization: Bearer <token>`\n * to outgoing requests when a session token is present. Register it with\n * `provideAuthdog()` or directly via `provideHttpClient(withInterceptors([authdogInterceptor]))`.\n *\n * Requests that already carry an `Authorization` header are left untouched.\n */\nexport const authdogInterceptor: HttpInterceptorFn = (req, next) => {\n const auth = inject(AuthdogService);\n const token = auth.token();\n\n if (!token || req.headers.has(\"Authorization\")) {\n return next(req);\n }\n\n return next(\n req.clone({\n setHeaders: { Authorization: `Bearer ${token}` },\n }),\n );\n};\n","import { inject } from \"@angular/core\";\nimport { Router, type CanActivateFn, type UrlTree } from \"@angular/router\";\nimport { AuthdogService } from \"./service\";\nimport { AUTHDOG_CONFIG } from \"./tokens\";\n\n/**\n * ⚠️ PRESENTATIONAL / UX ONLY — NOT A SECURITY BOUNDARY.\n *\n * This guard prevents an unauthenticated user from *navigating* to a route in\n * the SPA so they see a login redirect instead of a broken page. It runs\n * entirely in the browser and is therefore trivially bypassable by any client\n * (devtools, crafted requests, a patched bundle). It MUST NOT be relied on to\n * protect data or actions.\n *\n * Every protected operation MUST be independently enforced server-side: the\n * API behind the route has to validate the session on each request regardless\n * of what this guard decides.\n */\nexport const authdogGuard: CanActivateFn = (): boolean | UrlTree => {\n const auth = inject(AuthdogService);\n const router = inject(Router);\n const config = inject(AUTHDOG_CONFIG);\n\n // A token is sufficient for *navigation* — fetching the user is async and\n // we don't want to block routing on a network round-trip.\n if (auth.token()) {\n return true;\n }\n\n return router.parseUrl(config.loginPath ?? \"/\");\n};\n"],"mappings":"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","names":["makeEnvironmentProviders","InjectionToken","AUTHDOG_CONFIG","Injectable","computed","inject","signal","validateAndParsePublicKey","getPublicKeyPayload","publicKey","validatePublicKey","TOKEN_STORAGE_KEY","JWT_PATTERN","getTokenFromUri","url","fetchUserData","publicKey","token","validatePublicKey","publicKeyObj","getPublicKeyPayload","userData","buildAuthorizeUrl","redirectUri","options","authUrl","isBrowser","_AuthdogService_decorators","_init","Injectable","AuthdogService","inject","AUTHDOG_CONFIG","signal","computed","url","urlToken","JWT_PATTERN","TOKEN_STORAGE_KEY","existingToken","token","publicKey","redirectUrl","buildAuthorizeUrl","err","user","fetchUserData","__decoratorStart","__decorateElement","__runInitializers","provideAuthdog","config","makeEnvironmentProviders","AUTHDOG_CONFIG","AuthdogService","inject","authdogInterceptor","req","next","token","inject","AuthdogService","inject","Router","authdogGuard","auth","inject","AuthdogService","router","Router","config","AUTHDOG_CONFIG"]}
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@authdog/angular",
3
+ "version": "0.2.0",
4
+ "description": "Authdog Angular SDK",
5
+ "source": "src/index.ts",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.mjs",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.mjs",
13
+ "require": "./dist/index.js"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist/"
18
+ ],
19
+ "sideEffects": false,
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/authdog-labs/web-sdk.git",
23
+ "directory": "packages/angular"
24
+ },
25
+ "homepage": "https://github.com/authdog-labs/web-sdk/tree/main/packages/angular#readme",
26
+ "bugs": {
27
+ "url": "https://github.com/authdog-labs/web-sdk/issues"
28
+ },
29
+ "scripts": {
30
+ "format": "prettier --config .prettierrc.json --write \"**/*.{ts,md}\"",
31
+ "type-check": "tsc",
32
+ "clean": "rm -rf dist",
33
+ "build": "bun run clean && tsup",
34
+ "ship": "bun run build && bun publish --access public"
35
+ },
36
+ "dependencies": {
37
+ "@authdog/node-commons": "workspace:*"
38
+ },
39
+ "peerDependencies": {
40
+ "@angular/common": "^17.0.0 || ^18.0.0 || ^19.0.0",
41
+ "@angular/core": "^17.0.0 || ^18.0.0 || ^19.0.0",
42
+ "@angular/router": "^17.0.0 || ^18.0.0 || ^19.0.0",
43
+ "rxjs": "^7.8.0"
44
+ },
45
+ "devDependencies": {
46
+ "@angular/common": "^19.2.16",
47
+ "@angular/core": "^18.2.0",
48
+ "@angular/router": "^18.2.0",
49
+ "@types/node": "^22.10.5",
50
+ "dotenv": "^16.4.7",
51
+ "prettier": "^3.4.2",
52
+ "rxjs": "^7.8.1",
53
+ "tsup": "^8.3.5",
54
+ "typescript": "^5.7.2",
55
+ "vitest": "^2.1.8"
56
+ },
57
+ "publishConfig": {
58
+ "registry": "https://registry.npmjs.org/",
59
+ "access": "public"
60
+ }
61
+ }