@nuria-tech/auth-sdk 1.0.2 → 1.0.4
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 +207 -188
- package/dist/angular.cjs +49 -0
- package/dist/angular.cjs.map +1 -0
- package/dist/angular.d.cts +22 -0
- package/dist/angular.d.ts +22 -0
- package/dist/angular.js +47 -0
- package/dist/angular.js.map +1 -0
- package/dist/cookie-storage-adapter-EJeGX8Tl.d.ts +16 -0
- package/dist/cookie-storage-adapter-FKYkZ--Y.d.cts +16 -0
- package/dist/index.cjs +195 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -84
- package/dist/index.d.ts +4 -84
- package/dist/index.js +195 -24
- package/dist/index.js.map +1 -1
- package/dist/next.cjs +719 -0
- package/dist/next.cjs.map +1 -0
- package/dist/next.d.cts +12 -0
- package/dist/next.d.ts +12 -0
- package/dist/next.js +716 -0
- package/dist/next.js.map +1 -0
- package/dist/nuxt.cjs +719 -0
- package/dist/nuxt.cjs.map +1 -0
- package/dist/nuxt.d.cts +12 -0
- package/dist/nuxt.d.ts +12 -0
- package/dist/nuxt.js +716 -0
- package/dist/nuxt.js.map +1 -0
- package/dist/react.cjs +89 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +27 -0
- package/dist/react.d.ts +27 -0
- package/dist/react.js +85 -0
- package/dist/react.js.map +1 -0
- package/dist/types-2k4ZYpF4.d.cts +103 -0
- package/dist/types-2k4ZYpF4.d.ts +103 -0
- package/dist/vue.cjs +57 -0
- package/dist/vue.cjs.map +1 -0
- package/dist/vue.d.cts +13 -0
- package/dist/vue.d.ts +13 -0
- package/dist/vue.js +55 -0
- package/dist/vue.js.map +1 -0
- package/package.json +51 -2
package/README.md
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
# @nuria-tech/auth-sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@nuria-tech/auth-sdk)
|
|
4
|
+
[](https://github.com/nuria-tech/nuria-auth-sdk/actions/workflows/ci-publish.yml)
|
|
5
|
+
[](./LICENSE)
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
TypeScript SDK for OAuth 2.0 Authorization Code + PKCE, focused on browser apps and framework integrations (React, Vue, Nuxt, Next, Angular).
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
9
|
+
## Why this SDK
|
|
10
|
+
|
|
11
|
+
- PKCE S256 + state validation by default
|
|
12
|
+
- Redirect-only flow (no embedded credential UI)
|
|
13
|
+
- Optional automatic refresh with concurrency dedupe
|
|
14
|
+
- Storage adapters for browser/SSR scenarios
|
|
15
|
+
- Framework helpers in dedicated entrypoints
|
|
14
16
|
|
|
15
17
|
## Installation
|
|
16
18
|
|
|
@@ -20,254 +22,271 @@ npm install @nuria-tech/auth-sdk
|
|
|
20
22
|
|
|
21
23
|
Published on [npm](https://www.npmjs.com/package/@nuria-tech/auth-sdk).
|
|
22
24
|
|
|
23
|
-
##
|
|
25
|
+
## Entrypoints
|
|
26
|
+
|
|
27
|
+
- `@nuria-tech/auth-sdk`: core client + adapters
|
|
28
|
+
- `@nuria-tech/auth-sdk/react`: `useAuthSession`, `AuthProvider`, `useAuth`
|
|
29
|
+
- `@nuria-tech/auth-sdk/vue`: `useAuthSession` composable
|
|
30
|
+
- `@nuria-tech/auth-sdk/nuxt`: Nuxt cookie adapter helpers
|
|
31
|
+
- `@nuria-tech/auth-sdk/next`: Next cookie adapter helpers
|
|
32
|
+
- `@nuria-tech/auth-sdk/angular`: RxJS auth facade for Angular services/components
|
|
33
|
+
|
|
34
|
+
## Auth flows matrix
|
|
35
|
+
|
|
36
|
+
| Flow | Backend endpoint(s) | SDK method(s) | Result |
|
|
37
|
+
|---|---|---|---|
|
|
38
|
+
| Google | `POST /v2/google` | `loginWithGoogle(...)` | `Session` tokens |
|
|
39
|
+
| Login + password | `POST /v2/login` | `loginWithPassword(...)` | `Session` tokens |
|
|
40
|
+
| Code sent (default) | `POST /v2/login-code/challenge` + `POST /v2/2fa/verify-login` | `loginWithCodeSent(...)` + `completeLoginWithCode(...)` | `Session` tokens after code verify |
|
|
41
|
+
|
|
42
|
+
## Example apps
|
|
43
|
+
|
|
44
|
+
- `examples/react`
|
|
45
|
+
- `examples/vue`
|
|
46
|
+
- `examples/nuxt`
|
|
47
|
+
- `examples/next`
|
|
48
|
+
- `examples/angular`
|
|
49
|
+
|
|
50
|
+
## Core quick start
|
|
24
51
|
|
|
25
52
|
```ts
|
|
26
53
|
import { createAuthClient } from '@nuria-tech/auth-sdk';
|
|
27
54
|
|
|
28
55
|
const auth = createAuthClient({
|
|
29
56
|
clientId: 'your-client-id',
|
|
30
|
-
|
|
31
|
-
tokenEndpoint: 'https://your-auth-server.example.com/token',
|
|
32
|
-
redirectUri: 'https://your-app.example.com/callback',
|
|
33
|
-
scope: 'openid profile email',
|
|
57
|
+
redirectUri: `${window.location.origin}/callback`,
|
|
34
58
|
});
|
|
35
59
|
|
|
36
|
-
// Redirect to login
|
|
37
60
|
await auth.startLogin();
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
console.log(
|
|
61
|
+
// callback route
|
|
62
|
+
await auth.handleRedirectCallback(window.location.href);
|
|
63
|
+
const token = await auth.getAccessToken();
|
|
64
|
+
console.log(token);
|
|
42
65
|
```
|
|
43
66
|
|
|
44
|
-
##
|
|
67
|
+
## Default login flow (login code sent)
|
|
45
68
|
|
|
46
69
|
```ts
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
redirectUri: string;
|
|
53
|
-
|
|
54
|
-
// Optional
|
|
55
|
-
scope?: string; // default scope sent with every login
|
|
56
|
-
logoutEndpoint?: string; // if set, logout() redirects here
|
|
57
|
-
userinfoEndpoint?: string; // required for getUserinfo()
|
|
58
|
-
storage?: StorageAdapter; // default: MemoryStorageAdapter
|
|
59
|
-
transport?: AuthTransport; // default: FetchAuthTransport
|
|
60
|
-
onRedirect?: (url: string) => void | Promise<void>; // override browser redirect
|
|
61
|
-
enableRefreshToken?: boolean; // enable automatic token refresh
|
|
62
|
-
now?: () => number; // override Date.now() for testing
|
|
63
|
-
}
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
> **Security note:** Do not include `clientSecret` in browser apps. This SDK is designed for public clients (SPAs, mobile). PKCE provides the proof of possession without a client secret.
|
|
67
|
-
|
|
68
|
-
## Public API
|
|
69
|
-
|
|
70
|
-
```ts
|
|
71
|
-
interface AuthClient {
|
|
72
|
-
startLogin(options?: StartLoginOptions): Promise<void>;
|
|
73
|
-
handleRedirectCallback(callbackUrl?: string): Promise<Session>;
|
|
74
|
-
getSession(): Session | null;
|
|
75
|
-
getAccessToken(): Promise<string | null>;
|
|
76
|
-
logout(options?: { returnTo?: string }): Promise<void>;
|
|
77
|
-
isAuthenticated(): boolean;
|
|
78
|
-
onAuthStateChanged(handler: (session: Session | null) => void): () => void;
|
|
79
|
-
getUserinfo(): Promise<Record<string, unknown>>;
|
|
80
|
-
}
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
### `startLogin(options?)`
|
|
84
|
-
|
|
85
|
-
Generates PKCE `code_verifier` + `code_challenge` (S256), stores them in the configured storage, and redirects to `authorizationEndpoint`. The `state` parameter is always included and validated on callback.
|
|
70
|
+
const challenge = await auth.startLoginCodeChallenge({
|
|
71
|
+
email: 'user@company.com',
|
|
72
|
+
// optional: channel defaults to 'email'
|
|
73
|
+
// channel: 'sms',
|
|
74
|
+
});
|
|
86
75
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
loginHint: 'user@example.com',
|
|
91
|
-
extraParams: { prompt: 'login' },
|
|
76
|
+
const session = await auth.verifyLoginCode({
|
|
77
|
+
challengeId: challenge.challengeId,
|
|
78
|
+
code: '123456',
|
|
92
79
|
});
|
|
93
80
|
```
|
|
94
81
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
Parses the callback URL, validates `state`, exchanges `code` for tokens using a form-encoded POST to `tokenEndpoint`, and clears transient PKCE storage. Throws typed `AuthError` on any failure.
|
|
98
|
-
Token endpoint calls are sent with `credentials: 'include'` to support `HttpOnly` refresh-token cookies.
|
|
82
|
+
Aliases with clearer naming:
|
|
99
83
|
|
|
100
84
|
```ts
|
|
101
|
-
|
|
102
|
-
|
|
85
|
+
await auth.loginWithCodeSent({ email: 'user@company.com' });
|
|
86
|
+
await auth.completeLoginWithCode({ challengeId: '...', code: '123456' });
|
|
103
87
|
```
|
|
104
88
|
|
|
105
|
-
|
|
89
|
+
## React quick start
|
|
106
90
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
### `logout(options?)`
|
|
112
|
-
|
|
113
|
-
Clears the local session. If `logoutEndpoint` is configured, redirects to it. Validates `returnTo` to prevent open redirect attacks.
|
|
91
|
+
```tsx
|
|
92
|
+
import { createAuthClient } from '@nuria-tech/auth-sdk';
|
|
93
|
+
import { AuthProvider, useAuth } from '@nuria-tech/auth-sdk/react';
|
|
114
94
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
95
|
+
const auth = createAuthClient({
|
|
96
|
+
clientId: 'your-client-id',
|
|
97
|
+
redirectUri: `${window.location.origin}/callback`,
|
|
98
|
+
});
|
|
118
99
|
|
|
119
|
-
|
|
100
|
+
function AppContent() {
|
|
101
|
+
const { session, isLoading, login, logout } = useAuth();
|
|
120
102
|
|
|
121
|
-
|
|
103
|
+
if (isLoading) return <div>Loading...</div>;
|
|
104
|
+
if (!session) return <button onClick={() => login()}>Login</button>;
|
|
105
|
+
return <button onClick={() => logout()}>Logout</button>;
|
|
106
|
+
}
|
|
122
107
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
108
|
+
export function App() {
|
|
109
|
+
return (
|
|
110
|
+
<AuthProvider auth={auth}>
|
|
111
|
+
<AppContent />
|
|
112
|
+
</AuthProvider>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
127
115
|
```
|
|
128
116
|
|
|
129
|
-
##
|
|
130
|
-
|
|
131
|
-
Default is `MemoryStorageAdapter` (most secure for XSS resistance, clears on reload).
|
|
117
|
+
## Vue quick start
|
|
132
118
|
|
|
133
119
|
```ts
|
|
134
|
-
import {
|
|
120
|
+
import { createAuthClient } from '@nuria-tech/auth-sdk';
|
|
121
|
+
import { useAuthSession } from '@nuria-tech/auth-sdk/vue';
|
|
135
122
|
|
|
136
|
-
// sessionStorage: persists per tab, JS-readable
|
|
137
123
|
const auth = createAuthClient({
|
|
138
|
-
|
|
139
|
-
|
|
124
|
+
clientId: 'your-client-id',
|
|
125
|
+
redirectUri: `${window.location.origin}/callback`,
|
|
140
126
|
});
|
|
127
|
+
|
|
128
|
+
export function usePageAuth() {
|
|
129
|
+
const { session, isLoading, refresh } = useAuthSession(auth);
|
|
130
|
+
return { session, isLoading, refresh };
|
|
131
|
+
}
|
|
141
132
|
```
|
|
142
133
|
|
|
143
|
-
|
|
134
|
+
## Nuxt quick start
|
|
144
135
|
|
|
145
136
|
```ts
|
|
146
|
-
import {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
137
|
+
import { createNuxtAuthClient } from '@nuria-tech/auth-sdk/nuxt';
|
|
138
|
+
import { useCookie } from '#app';
|
|
139
|
+
|
|
140
|
+
const auth = createNuxtAuthClient(
|
|
141
|
+
{
|
|
142
|
+
clientId: process.env.NUXT_PUBLIC_AUTH_CLIENT_ID!,
|
|
143
|
+
redirectUri: process.env.NUXT_PUBLIC_AUTH_CALLBACK_URL!,
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
get: (name) => useCookie<string | null>(name).value,
|
|
147
|
+
set: (name, value) => {
|
|
148
|
+
useCookie<string | null>(name).value = value;
|
|
149
|
+
},
|
|
150
|
+
remove: (name) => {
|
|
151
|
+
useCookie<string | null>(name).value = null;
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
);
|
|
156
155
|
```
|
|
157
156
|
|
|
158
|
-
##
|
|
157
|
+
## Next quick start
|
|
159
158
|
|
|
160
159
|
```ts
|
|
161
|
-
import {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}
|
|
160
|
+
import { createNextAuthClient } from '@nuria-tech/auth-sdk/next';
|
|
161
|
+
import { cookies } from 'next/headers';
|
|
162
|
+
|
|
163
|
+
export function createServerAuth() {
|
|
164
|
+
const cookieStore = cookies();
|
|
165
|
+
return createNextAuthClient(
|
|
166
|
+
{
|
|
167
|
+
clientId: process.env.NEXT_PUBLIC_AUTH_CLIENT_ID!,
|
|
168
|
+
redirectUri: process.env.NEXT_PUBLIC_AUTH_CALLBACK_URL!,
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
get: (name) => cookieStore.get(name)?.value,
|
|
172
|
+
set: (name, value) => cookieStore.set(name, value),
|
|
173
|
+
remove: (name) => cookieStore.delete(name),
|
|
174
|
+
},
|
|
175
|
+
);
|
|
176
|
+
}
|
|
178
177
|
```
|
|
179
178
|
|
|
180
|
-
##
|
|
179
|
+
## Angular quick start
|
|
181
180
|
|
|
182
|
-
```
|
|
183
|
-
import {
|
|
181
|
+
```ts
|
|
182
|
+
import { Injectable } from '@angular/core';
|
|
184
183
|
import { createAuthClient } from '@nuria-tech/auth-sdk';
|
|
184
|
+
import { createAngularAuthFacade } from '@nuria-tech/auth-sdk/angular';
|
|
185
185
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
});
|
|
186
|
+
@Injectable({ providedIn: 'root' })
|
|
187
|
+
export class AuthService {
|
|
188
|
+
private auth = createAuthClient({
|
|
189
|
+
clientId: 'your-client-id',
|
|
190
|
+
redirectUri: `${window.location.origin}/callback`,
|
|
191
|
+
});
|
|
192
192
|
|
|
193
|
-
|
|
194
|
-
|
|
193
|
+
private facade = createAngularAuthFacade(this.auth);
|
|
194
|
+
state$ = this.facade.state$;
|
|
195
195
|
|
|
196
|
-
|
|
196
|
+
login() {
|
|
197
|
+
return this.facade.login();
|
|
198
|
+
}
|
|
197
199
|
|
|
198
|
-
|
|
199
|
-
return
|
|
200
|
+
logout() {
|
|
201
|
+
return this.facade.logout();
|
|
200
202
|
}
|
|
201
|
-
return <button onClick={() => auth.logout()}>Logout</button>;
|
|
202
203
|
}
|
|
203
204
|
```
|
|
204
205
|
|
|
205
|
-
|
|
206
|
+
Full Angular example (service + guard + callback route + status component):
|
|
207
|
+
`examples/angular`
|
|
208
|
+
|
|
209
|
+
## Defaults
|
|
210
|
+
|
|
211
|
+
- `baseUrl`: `https://ms-auth-v2.nuria.com.br`
|
|
212
|
+
- `authorizationEndpoint`: `${baseUrl}/v2/oauth/authorize`
|
|
213
|
+
- `tokenEndpoint`: `${baseUrl}/v2/oauth/token`
|
|
214
|
+
- `scope`: `openid profile email`
|
|
215
|
+
- `enableRefreshToken`: `true`
|
|
216
|
+
|
|
217
|
+
## Configuration
|
|
206
218
|
|
|
207
219
|
```ts
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
getCookie: async (name) => cookieApi.get(name) ?? null,
|
|
223
|
-
setCookie: async (name, value) => cookieApi.set(name, value),
|
|
224
|
-
removeCookie: async (name) => cookieApi.delete(name),
|
|
225
|
-
}),
|
|
226
|
-
onRedirect: (url) => { redirect(url); },
|
|
227
|
-
});
|
|
220
|
+
interface AuthConfig {
|
|
221
|
+
clientId: string;
|
|
222
|
+
redirectUri: string;
|
|
223
|
+
baseUrl?: string;
|
|
224
|
+
authorizationEndpoint?: string;
|
|
225
|
+
tokenEndpoint?: string;
|
|
226
|
+
scope?: string;
|
|
227
|
+
logoutEndpoint?: string;
|
|
228
|
+
userinfoEndpoint?: string;
|
|
229
|
+
storage?: StorageAdapter;
|
|
230
|
+
transport?: AuthTransport;
|
|
231
|
+
onRedirect?: (url: string) => void | Promise<void>;
|
|
232
|
+
enableRefreshToken?: boolean;
|
|
233
|
+
now?: () => number;
|
|
228
234
|
}
|
|
229
235
|
```
|
|
230
236
|
|
|
231
|
-
##
|
|
237
|
+
## Storage strategy
|
|
232
238
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
- **Validate `returnTo`:** The `logout()` method rejects non-`https://` or `http://` `returnTo` values.
|
|
239
|
+
| Adapter | Persists reload | JS-readable | SSR |
|
|
240
|
+
|---|---|---|---|
|
|
241
|
+
| `MemoryStorageAdapter` | No | Yes | No |
|
|
242
|
+
| `WebStorageAdapter(sessionStorage)` | Per tab | Yes | No |
|
|
243
|
+
| `WebStorageAdapter(localStorage)` | Yes | Yes | No |
|
|
244
|
+
| `CookieStorageAdapter` | Configurable | Depends on cookie flags | Yes |
|
|
240
245
|
|
|
241
|
-
##
|
|
246
|
+
## Security notes
|
|
242
247
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
248
|
+
- Do not use `clientSecret` in browser/mobile apps.
|
|
249
|
+
- Prefer memory storage when possible.
|
|
250
|
+
- Keep refresh on cookies (`HttpOnly`) server-side when available.
|
|
251
|
+
- `logout({ returnTo })` accepts `https://` URLs, plus `http://localhost|127.0.0.1|[::1]` for local dev only.
|
|
252
|
+
- `logout({ returnTo })` rejects URLs with embedded credentials (`user:pass@host`).
|
|
253
|
+
- `isAuthenticated()` also checks token expiration (`expiresAt`) when available.
|
|
254
|
+
- Browser cookie storage encodes/decodes values safely (`encodeURIComponent`/`decodeURIComponent`).
|
|
249
255
|
|
|
250
|
-
|
|
256
|
+
Full policy and reporting process: [SECURITY.md](./SECURITY.md).
|
|
251
257
|
|
|
252
|
-
|
|
253
|
-
- **Missing code_verifier:** The `nuria:oauth:code_verifier` key was not found in storage. Ensure `startLogin()` was called before `handleRedirectCallback()`.
|
|
254
|
-
- **Token refresh fails:** Ensure `enableRefreshToken: true` is set and the auth server supports the `refresh_token` grant for your client.
|
|
258
|
+
## Public API
|
|
255
259
|
|
|
256
|
-
|
|
260
|
+
```ts
|
|
261
|
+
interface AuthClient {
|
|
262
|
+
startLogin(options?: StartLoginOptions): Promise<void>;
|
|
263
|
+
handleRedirectCallback(callbackUrl?: string): Promise<Session>;
|
|
264
|
+
getSession(): Session | null;
|
|
265
|
+
getAccessToken(): Promise<string | null>;
|
|
266
|
+
logout(options?: { returnTo?: string }): Promise<void>;
|
|
267
|
+
isAuthenticated(): boolean;
|
|
268
|
+
onAuthStateChanged(handler: (session: Session | null) => void): () => void;
|
|
269
|
+
getUserinfo(): Promise<Record<string, unknown>>;
|
|
270
|
+
startLoginCodeChallenge(options: LoginCodeChallengeOptions): Promise<TwoFactorChallenge>;
|
|
271
|
+
verifyLoginCode(options: VerifyLoginCodeOptions): Promise<Session>;
|
|
272
|
+
loginWithCodeSent(options: LoginCodeChallengeOptions): Promise<TwoFactorChallenge>;
|
|
273
|
+
completeLoginWithCode(options: VerifyLoginCodeOptions): Promise<Session>;
|
|
274
|
+
loginWithGoogle(options: GoogleLoginOptions): Promise<Session>;
|
|
275
|
+
loginWithPassword(options: PasswordLoginOptions): Promise<Session>;
|
|
276
|
+
}
|
|
277
|
+
```
|
|
257
278
|
|
|
258
|
-
|
|
279
|
+
## CI and publish
|
|
259
280
|
|
|
260
|
-
-
|
|
261
|
-
-
|
|
281
|
+
- PR/main runs: typecheck, lint, test, build
|
|
282
|
+
- Tag `v*` runs publish workflow with Trusted Publishing
|
|
262
283
|
|
|
263
|
-
|
|
284
|
+
Publish flow:
|
|
264
285
|
|
|
265
286
|
1. Update `version` in `package.json`
|
|
266
|
-
2.
|
|
267
|
-
3.
|
|
268
|
-
|
|
269
|
-
> **One-time setup:** after the first manual publish, configure Trusted Publishing at **npmjs.com → package → Settings → Automated Publishing** with repository `nuria-tech/nuria-auth-sdk` and workflow `ci-publish.yml`.
|
|
287
|
+
2. Tag and push (`git tag vX.Y.Z && git push --tags`)
|
|
288
|
+
3. Workflow validates and publishes
|
|
270
289
|
|
|
271
290
|
## License
|
|
272
291
|
|
|
273
|
-
MIT
|
|
292
|
+
MIT - see [LICENSE](./LICENSE).
|
package/dist/angular.cjs
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var rxjs = require('rxjs');
|
|
4
|
+
|
|
5
|
+
// src/angular/index.ts
|
|
6
|
+
function toState(session, isLoading, error) {
|
|
7
|
+
return {
|
|
8
|
+
session,
|
|
9
|
+
isAuthenticated: session !== null,
|
|
10
|
+
isLoading,
|
|
11
|
+
error
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function createAngularAuthFacade(auth) {
|
|
15
|
+
const subject = new rxjs.BehaviorSubject(
|
|
16
|
+
toState(auth.getSession(), auth.getSession() === null, null)
|
|
17
|
+
);
|
|
18
|
+
const unsubscribe = auth.onAuthStateChanged((nextSession) => {
|
|
19
|
+
subject.next(toState(nextSession, false, null));
|
|
20
|
+
});
|
|
21
|
+
const refresh = async () => {
|
|
22
|
+
subject.next(toState(subject.value.session, true, null));
|
|
23
|
+
try {
|
|
24
|
+
await auth.getAccessToken();
|
|
25
|
+
const session = auth.getSession();
|
|
26
|
+
subject.next(toState(session, false, null));
|
|
27
|
+
return session;
|
|
28
|
+
} catch (error) {
|
|
29
|
+
subject.next(toState(auth.getSession(), false, error));
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
void refresh();
|
|
34
|
+
return {
|
|
35
|
+
state$: subject.asObservable(),
|
|
36
|
+
snapshot: () => subject.value,
|
|
37
|
+
refresh,
|
|
38
|
+
login: () => auth.startLogin(),
|
|
39
|
+
logout: (options) => auth.logout(options),
|
|
40
|
+
destroy: () => {
|
|
41
|
+
unsubscribe();
|
|
42
|
+
subject.complete();
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
exports.createAngularAuthFacade = createAngularAuthFacade;
|
|
48
|
+
//# sourceMappingURL=angular.cjs.map
|
|
49
|
+
//# sourceMappingURL=angular.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/angular/index.ts"],"names":["BehaviorSubject"],"mappings":";;;;;AAmBA,SAAS,OAAA,CACP,OAAA,EACA,SAAA,EACA,KAAA,EACkB;AAClB,EAAA,OAAO;AAAA,IACL,OAAA;AAAA,IACA,iBAAiB,OAAA,KAAY,IAAA;AAAA,IAC7B,SAAA;AAAA,IACA;AAAA,GACF;AACF;AAEO,SAAS,wBAAwB,IAAA,EAAqC;AAC3E,EAAA,MAAM,UAAU,IAAIA,oBAAA;AAAA,IAClB,OAAA,CAAQ,KAAK,UAAA,EAAW,EAAG,KAAK,UAAA,EAAW,KAAM,MAAM,IAAI;AAAA,GAC7D;AAEA,EAAA,MAAM,WAAA,GAAc,IAAA,CAAK,kBAAA,CAAmB,CAAC,WAAA,KAAgB;AAC3D,IAAA,OAAA,CAAQ,IAAA,CAAK,OAAA,CAAQ,WAAA,EAAa,KAAA,EAAO,IAAI,CAAC,CAAA;AAAA,EAChD,CAAC,CAAA;AAED,EAAA,MAAM,UAAU,YAAqC;AACnD,IAAA,OAAA,CAAQ,KAAK,OAAA,CAAQ,OAAA,CAAQ,MAAM,OAAA,EAAS,IAAA,EAAM,IAAI,CAAC,CAAA;AACvD,IAAA,IAAI;AACF,MAAA,MAAM,KAAK,cAAA,EAAe;AAC1B,MAAA,MAAM,OAAA,GAAU,KAAK,UAAA,EAAW;AAChC,MAAA,OAAA,CAAQ,IAAA,CAAK,OAAA,CAAQ,OAAA,EAAS,KAAA,EAAO,IAAI,CAAC,CAAA;AAC1C,MAAA,OAAO,OAAA;AAAA,IACT,SAAS,KAAA,EAAO;AACd,MAAA,OAAA,CAAQ,KAAK,OAAA,CAAQ,IAAA,CAAK,YAAW,EAAG,KAAA,EAAO,KAAK,CAAC,CAAA;AACrD,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,EACF,CAAA;AAEA,EAAA,KAAK,OAAA,EAAQ;AAEb,EAAA,OAAO;AAAA,IACL,MAAA,EAAQ,QAAQ,YAAA,EAAa;AAAA,IAC7B,QAAA,EAAU,MAAM,OAAA,CAAQ,KAAA;AAAA,IACxB,OAAA;AAAA,IACA,KAAA,EAAO,MAAM,IAAA,CAAK,UAAA,EAAW;AAAA,IAC7B,MAAA,EAAQ,CAAC,OAAA,KAAY,IAAA,CAAK,OAAO,OAAO,CAAA;AAAA,IACxC,SAAS,MAAM;AACb,MAAA,WAAA,EAAY;AACZ,MAAA,OAAA,CAAQ,QAAA,EAAS;AAAA,IACnB;AAAA,GACF;AACF","file":"angular.cjs","sourcesContent":["import { BehaviorSubject, type Observable } from 'rxjs';\nimport type { AuthClient, Session } from '../core/types';\n\nexport interface AngularAuthState {\n session: Session | null;\n isAuthenticated: boolean;\n isLoading: boolean;\n error: unknown;\n}\n\nexport interface AngularAuthFacade {\n state$: Observable<AngularAuthState>;\n snapshot: () => AngularAuthState;\n refresh: () => Promise<Session | null>;\n login: () => Promise<void>;\n logout: (options?: { returnTo?: string }) => Promise<void>;\n destroy: () => void;\n}\n\nfunction toState(\n session: Session | null,\n isLoading: boolean,\n error: unknown,\n): AngularAuthState {\n return {\n session,\n isAuthenticated: session !== null,\n isLoading,\n error,\n };\n}\n\nexport function createAngularAuthFacade(auth: AuthClient): AngularAuthFacade {\n const subject = new BehaviorSubject<AngularAuthState>(\n toState(auth.getSession(), auth.getSession() === null, null),\n );\n\n const unsubscribe = auth.onAuthStateChanged((nextSession) => {\n subject.next(toState(nextSession, false, null));\n });\n\n const refresh = async (): Promise<Session | null> => {\n subject.next(toState(subject.value.session, true, null));\n try {\n await auth.getAccessToken();\n const session = auth.getSession();\n subject.next(toState(session, false, null));\n return session;\n } catch (error) {\n subject.next(toState(auth.getSession(), false, error));\n return null;\n }\n };\n\n void refresh();\n\n return {\n state$: subject.asObservable(),\n snapshot: () => subject.value,\n refresh,\n login: () => auth.startLogin(),\n logout: (options) => auth.logout(options),\n destroy: () => {\n unsubscribe();\n subject.complete();\n },\n };\n}\n"]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Observable } from 'rxjs';
|
|
2
|
+
import { e as Session, a as AuthClient } from './types-2k4ZYpF4.cjs';
|
|
3
|
+
|
|
4
|
+
interface AngularAuthState {
|
|
5
|
+
session: Session | null;
|
|
6
|
+
isAuthenticated: boolean;
|
|
7
|
+
isLoading: boolean;
|
|
8
|
+
error: unknown;
|
|
9
|
+
}
|
|
10
|
+
interface AngularAuthFacade {
|
|
11
|
+
state$: Observable<AngularAuthState>;
|
|
12
|
+
snapshot: () => AngularAuthState;
|
|
13
|
+
refresh: () => Promise<Session | null>;
|
|
14
|
+
login: () => Promise<void>;
|
|
15
|
+
logout: (options?: {
|
|
16
|
+
returnTo?: string;
|
|
17
|
+
}) => Promise<void>;
|
|
18
|
+
destroy: () => void;
|
|
19
|
+
}
|
|
20
|
+
declare function createAngularAuthFacade(auth: AuthClient): AngularAuthFacade;
|
|
21
|
+
|
|
22
|
+
export { type AngularAuthFacade, type AngularAuthState, createAngularAuthFacade };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Observable } from 'rxjs';
|
|
2
|
+
import { e as Session, a as AuthClient } from './types-2k4ZYpF4.js';
|
|
3
|
+
|
|
4
|
+
interface AngularAuthState {
|
|
5
|
+
session: Session | null;
|
|
6
|
+
isAuthenticated: boolean;
|
|
7
|
+
isLoading: boolean;
|
|
8
|
+
error: unknown;
|
|
9
|
+
}
|
|
10
|
+
interface AngularAuthFacade {
|
|
11
|
+
state$: Observable<AngularAuthState>;
|
|
12
|
+
snapshot: () => AngularAuthState;
|
|
13
|
+
refresh: () => Promise<Session | null>;
|
|
14
|
+
login: () => Promise<void>;
|
|
15
|
+
logout: (options?: {
|
|
16
|
+
returnTo?: string;
|
|
17
|
+
}) => Promise<void>;
|
|
18
|
+
destroy: () => void;
|
|
19
|
+
}
|
|
20
|
+
declare function createAngularAuthFacade(auth: AuthClient): AngularAuthFacade;
|
|
21
|
+
|
|
22
|
+
export { type AngularAuthFacade, type AngularAuthState, createAngularAuthFacade };
|
package/dist/angular.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { BehaviorSubject } from 'rxjs';
|
|
2
|
+
|
|
3
|
+
// src/angular/index.ts
|
|
4
|
+
function toState(session, isLoading, error) {
|
|
5
|
+
return {
|
|
6
|
+
session,
|
|
7
|
+
isAuthenticated: session !== null,
|
|
8
|
+
isLoading,
|
|
9
|
+
error
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
function createAngularAuthFacade(auth) {
|
|
13
|
+
const subject = new BehaviorSubject(
|
|
14
|
+
toState(auth.getSession(), auth.getSession() === null, null)
|
|
15
|
+
);
|
|
16
|
+
const unsubscribe = auth.onAuthStateChanged((nextSession) => {
|
|
17
|
+
subject.next(toState(nextSession, false, null));
|
|
18
|
+
});
|
|
19
|
+
const refresh = async () => {
|
|
20
|
+
subject.next(toState(subject.value.session, true, null));
|
|
21
|
+
try {
|
|
22
|
+
await auth.getAccessToken();
|
|
23
|
+
const session = auth.getSession();
|
|
24
|
+
subject.next(toState(session, false, null));
|
|
25
|
+
return session;
|
|
26
|
+
} catch (error) {
|
|
27
|
+
subject.next(toState(auth.getSession(), false, error));
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
void refresh();
|
|
32
|
+
return {
|
|
33
|
+
state$: subject.asObservable(),
|
|
34
|
+
snapshot: () => subject.value,
|
|
35
|
+
refresh,
|
|
36
|
+
login: () => auth.startLogin(),
|
|
37
|
+
logout: (options) => auth.logout(options),
|
|
38
|
+
destroy: () => {
|
|
39
|
+
unsubscribe();
|
|
40
|
+
subject.complete();
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export { createAngularAuthFacade };
|
|
46
|
+
//# sourceMappingURL=angular.js.map
|
|
47
|
+
//# sourceMappingURL=angular.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/angular/index.ts"],"names":[],"mappings":";;;AAmBA,SAAS,OAAA,CACP,OAAA,EACA,SAAA,EACA,KAAA,EACkB;AAClB,EAAA,OAAO;AAAA,IACL,OAAA;AAAA,IACA,iBAAiB,OAAA,KAAY,IAAA;AAAA,IAC7B,SAAA;AAAA,IACA;AAAA,GACF;AACF;AAEO,SAAS,wBAAwB,IAAA,EAAqC;AAC3E,EAAA,MAAM,UAAU,IAAI,eAAA;AAAA,IAClB,OAAA,CAAQ,KAAK,UAAA,EAAW,EAAG,KAAK,UAAA,EAAW,KAAM,MAAM,IAAI;AAAA,GAC7D;AAEA,EAAA,MAAM,WAAA,GAAc,IAAA,CAAK,kBAAA,CAAmB,CAAC,WAAA,KAAgB;AAC3D,IAAA,OAAA,CAAQ,IAAA,CAAK,OAAA,CAAQ,WAAA,EAAa,KAAA,EAAO,IAAI,CAAC,CAAA;AAAA,EAChD,CAAC,CAAA;AAED,EAAA,MAAM,UAAU,YAAqC;AACnD,IAAA,OAAA,CAAQ,KAAK,OAAA,CAAQ,OAAA,CAAQ,MAAM,OAAA,EAAS,IAAA,EAAM,IAAI,CAAC,CAAA;AACvD,IAAA,IAAI;AACF,MAAA,MAAM,KAAK,cAAA,EAAe;AAC1B,MAAA,MAAM,OAAA,GAAU,KAAK,UAAA,EAAW;AAChC,MAAA,OAAA,CAAQ,IAAA,CAAK,OAAA,CAAQ,OAAA,EAAS,KAAA,EAAO,IAAI,CAAC,CAAA;AAC1C,MAAA,OAAO,OAAA;AAAA,IACT,SAAS,KAAA,EAAO;AACd,MAAA,OAAA,CAAQ,KAAK,OAAA,CAAQ,IAAA,CAAK,YAAW,EAAG,KAAA,EAAO,KAAK,CAAC,CAAA;AACrD,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,EACF,CAAA;AAEA,EAAA,KAAK,OAAA,EAAQ;AAEb,EAAA,OAAO;AAAA,IACL,MAAA,EAAQ,QAAQ,YAAA,EAAa;AAAA,IAC7B,QAAA,EAAU,MAAM,OAAA,CAAQ,KAAA;AAAA,IACxB,OAAA;AAAA,IACA,KAAA,EAAO,MAAM,IAAA,CAAK,UAAA,EAAW;AAAA,IAC7B,MAAA,EAAQ,CAAC,OAAA,KAAY,IAAA,CAAK,OAAO,OAAO,CAAA;AAAA,IACxC,SAAS,MAAM;AACb,MAAA,WAAA,EAAY;AACZ,MAAA,OAAA,CAAQ,QAAA,EAAS;AAAA,IACnB;AAAA,GACF;AACF","file":"angular.js","sourcesContent":["import { BehaviorSubject, type Observable } from 'rxjs';\nimport type { AuthClient, Session } from '../core/types';\n\nexport interface AngularAuthState {\n session: Session | null;\n isAuthenticated: boolean;\n isLoading: boolean;\n error: unknown;\n}\n\nexport interface AngularAuthFacade {\n state$: Observable<AngularAuthState>;\n snapshot: () => AngularAuthState;\n refresh: () => Promise<Session | null>;\n login: () => Promise<void>;\n logout: (options?: { returnTo?: string }) => Promise<void>;\n destroy: () => void;\n}\n\nfunction toState(\n session: Session | null,\n isLoading: boolean,\n error: unknown,\n): AngularAuthState {\n return {\n session,\n isAuthenticated: session !== null,\n isLoading,\n error,\n };\n}\n\nexport function createAngularAuthFacade(auth: AuthClient): AngularAuthFacade {\n const subject = new BehaviorSubject<AngularAuthState>(\n toState(auth.getSession(), auth.getSession() === null, null),\n );\n\n const unsubscribe = auth.onAuthStateChanged((nextSession) => {\n subject.next(toState(nextSession, false, null));\n });\n\n const refresh = async (): Promise<Session | null> => {\n subject.next(toState(subject.value.session, true, null));\n try {\n await auth.getAccessToken();\n const session = auth.getSession();\n subject.next(toState(session, false, null));\n return session;\n } catch (error) {\n subject.next(toState(auth.getSession(), false, error));\n return null;\n }\n };\n\n void refresh();\n\n return {\n state$: subject.asObservable(),\n snapshot: () => subject.value,\n refresh,\n login: () => auth.startLogin(),\n logout: (options) => auth.logout(options),\n destroy: () => {\n unsubscribe();\n subject.complete();\n },\n };\n}\n"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { S as StorageAdapter } from './types-2k4ZYpF4.js';
|
|
2
|
+
|
|
3
|
+
interface CookieStorageCallbacks {
|
|
4
|
+
getCookie(name: string): string | null | Promise<string | null>;
|
|
5
|
+
setCookie(name: string, value: string): void | Promise<void>;
|
|
6
|
+
removeCookie(name: string): void | Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
declare class CookieStorageAdapter implements StorageAdapter {
|
|
9
|
+
private readonly callbacks;
|
|
10
|
+
constructor(callbacks: CookieStorageCallbacks);
|
|
11
|
+
get(key: string): Promise<string | null>;
|
|
12
|
+
set(key: string, value: string): Promise<void>;
|
|
13
|
+
remove(key: string): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export { CookieStorageAdapter as C };
|