@nuria-tech/auth-sdk 1.0.3 → 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 +204 -222
- 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 +145 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -85
- package/dist/index.d.ts +4 -85
- package/dist/index.js +145 -16
- 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,291 +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
|
-
##
|
|
24
|
-
|
|
25
|
-
```ts
|
|
26
|
-
import { createAuthClient } from '@nuria-tech/auth-sdk';
|
|
27
|
-
|
|
28
|
-
const auth = createAuthClient({
|
|
29
|
-
clientId: 'your-client-id',
|
|
30
|
-
baseUrl: 'https://ms-auth-v2.nuria.com.br',
|
|
31
|
-
redirectUri: 'https://your-app.example.com/callback',
|
|
32
|
-
});
|
|
25
|
+
## Entrypoints
|
|
33
26
|
|
|
34
|
-
|
|
35
|
-
|
|
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
|
|
36
33
|
|
|
37
|
-
|
|
38
|
-
const session = await auth.handleRedirectCallback(window.location.href);
|
|
39
|
-
console.log(session.tokens.accessToken);
|
|
40
|
-
```
|
|
34
|
+
## Auth flows matrix
|
|
41
35
|
|
|
42
|
-
|
|
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 |
|
|
43
41
|
|
|
44
|
-
|
|
42
|
+
## Example apps
|
|
45
43
|
|
|
46
|
-
- `
|
|
47
|
-
- `
|
|
48
|
-
- `
|
|
49
|
-
- `
|
|
50
|
-
- `
|
|
44
|
+
- `examples/react`
|
|
45
|
+
- `examples/vue`
|
|
46
|
+
- `examples/nuxt`
|
|
47
|
+
- `examples/next`
|
|
48
|
+
- `examples/angular`
|
|
51
49
|
|
|
52
|
-
|
|
50
|
+
## Core quick start
|
|
53
51
|
|
|
54
52
|
```ts
|
|
53
|
+
import { createAuthClient } from '@nuria-tech/auth-sdk';
|
|
54
|
+
|
|
55
55
|
const auth = createAuthClient({
|
|
56
56
|
clientId: 'your-client-id',
|
|
57
|
-
|
|
58
|
-
authorizationEndpoint: 'https://auth.hml.nuria.com.br/custom/authorize',
|
|
59
|
-
tokenEndpoint: 'https://auth.hml.nuria.com.br/custom/token',
|
|
60
|
-
redirectUri: 'https://your-app.example.com/callback',
|
|
61
|
-
scope: 'openid profile',
|
|
62
|
-
enableRefreshToken: false,
|
|
57
|
+
redirectUri: `${window.location.origin}/callback`,
|
|
63
58
|
});
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
## Configuration
|
|
67
59
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
// Optional
|
|
75
|
-
baseUrl?: string; // default: https://ms-auth-v2.nuria.com.br
|
|
76
|
-
authorizationEndpoint?: string; // default: {baseUrl}/v2/oauth/authorize
|
|
77
|
-
tokenEndpoint?: string; // default: {baseUrl}/v2/oauth/token
|
|
78
|
-
scope?: string; // default: "openid profile email"
|
|
79
|
-
logoutEndpoint?: string; // if set, logout() redirects here
|
|
80
|
-
userinfoEndpoint?: string; // required for getUserinfo()
|
|
81
|
-
storage?: StorageAdapter; // default: MemoryStorageAdapter
|
|
82
|
-
transport?: AuthTransport; // default: FetchAuthTransport
|
|
83
|
-
onRedirect?: (url: string) => void | Promise<void>; // override browser redirect
|
|
84
|
-
enableRefreshToken?: boolean; // default: true
|
|
85
|
-
now?: () => number; // override Date.now() for testing
|
|
86
|
-
}
|
|
60
|
+
await auth.startLogin();
|
|
61
|
+
// callback route
|
|
62
|
+
await auth.handleRedirectCallback(window.location.href);
|
|
63
|
+
const token = await auth.getAccessToken();
|
|
64
|
+
console.log(token);
|
|
87
65
|
```
|
|
88
66
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
## Public API
|
|
67
|
+
## Default login flow (login code sent)
|
|
92
68
|
|
|
93
69
|
```ts
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
logout(options?: { returnTo?: string }): Promise<void>;
|
|
100
|
-
isAuthenticated(): boolean;
|
|
101
|
-
onAuthStateChanged(handler: (session: Session | null) => void): () => void;
|
|
102
|
-
getUserinfo(): Promise<Record<string, unknown>>;
|
|
103
|
-
}
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
### `startLogin(options?)`
|
|
107
|
-
|
|
108
|
-
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
|
+
});
|
|
109
75
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
loginHint: 'user@example.com',
|
|
114
|
-
extraParams: { prompt: 'login' },
|
|
76
|
+
const session = await auth.verifyLoginCode({
|
|
77
|
+
challengeId: challenge.challengeId,
|
|
78
|
+
code: '123456',
|
|
115
79
|
});
|
|
116
80
|
```
|
|
117
81
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
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.
|
|
121
|
-
Token endpoint calls are sent with `credentials: 'include'` to support `HttpOnly` refresh-token cookies.
|
|
82
|
+
Aliases with clearer naming:
|
|
122
83
|
|
|
123
84
|
```ts
|
|
124
|
-
|
|
125
|
-
|
|
85
|
+
await auth.loginWithCodeSent({ email: 'user@company.com' });
|
|
86
|
+
await auth.completeLoginWithCode({ challengeId: '...', code: '123456' });
|
|
126
87
|
```
|
|
127
88
|
|
|
128
|
-
|
|
89
|
+
## React quick start
|
|
129
90
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
### `logout(options?)`
|
|
135
|
-
|
|
136
|
-
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';
|
|
137
94
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
95
|
+
const auth = createAuthClient({
|
|
96
|
+
clientId: 'your-client-id',
|
|
97
|
+
redirectUri: `${window.location.origin}/callback`,
|
|
98
|
+
});
|
|
141
99
|
|
|
142
|
-
|
|
100
|
+
function AppContent() {
|
|
101
|
+
const { session, isLoading, login, logout } = useAuth();
|
|
143
102
|
|
|
144
|
-
|
|
103
|
+
if (isLoading) return <div>Loading...</div>;
|
|
104
|
+
if (!session) return <button onClick={() => login()}>Login</button>;
|
|
105
|
+
return <button onClick={() => logout()}>Logout</button>;
|
|
106
|
+
}
|
|
145
107
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
108
|
+
export function App() {
|
|
109
|
+
return (
|
|
110
|
+
<AuthProvider auth={auth}>
|
|
111
|
+
<AppContent />
|
|
112
|
+
</AuthProvider>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
150
115
|
```
|
|
151
116
|
|
|
152
|
-
##
|
|
153
|
-
|
|
154
|
-
Default is `MemoryStorageAdapter` (most secure for XSS resistance, clears on reload).
|
|
117
|
+
## Vue quick start
|
|
155
118
|
|
|
156
119
|
```ts
|
|
157
|
-
import {
|
|
120
|
+
import { createAuthClient } from '@nuria-tech/auth-sdk';
|
|
121
|
+
import { useAuthSession } from '@nuria-tech/auth-sdk/vue';
|
|
158
122
|
|
|
159
|
-
// sessionStorage: persists per tab, JS-readable
|
|
160
123
|
const auth = createAuthClient({
|
|
161
|
-
|
|
162
|
-
|
|
124
|
+
clientId: 'your-client-id',
|
|
125
|
+
redirectUri: `${window.location.origin}/callback`,
|
|
163
126
|
});
|
|
127
|
+
|
|
128
|
+
export function usePageAuth() {
|
|
129
|
+
const { session, isLoading, refresh } = useAuthSession(auth);
|
|
130
|
+
return { session, isLoading, refresh };
|
|
131
|
+
}
|
|
164
132
|
```
|
|
165
133
|
|
|
166
|
-
|
|
134
|
+
## Nuxt quick start
|
|
167
135
|
|
|
168
136
|
```ts
|
|
169
|
-
import {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
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
|
+
);
|
|
179
155
|
```
|
|
180
156
|
|
|
181
|
-
##
|
|
157
|
+
## Next quick start
|
|
182
158
|
|
|
183
159
|
```ts
|
|
184
|
-
import {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
}
|
|
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
|
+
}
|
|
201
177
|
```
|
|
202
178
|
|
|
203
|
-
##
|
|
179
|
+
## Angular quick start
|
|
204
180
|
|
|
205
|
-
```
|
|
206
|
-
import {
|
|
181
|
+
```ts
|
|
182
|
+
import { Injectable } from '@angular/core';
|
|
207
183
|
import { createAuthClient } from '@nuria-tech/auth-sdk';
|
|
184
|
+
import { createAngularAuthFacade } from '@nuria-tech/auth-sdk/angular';
|
|
208
185
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
}
|
|
186
|
+
@Injectable({ providedIn: 'root' })
|
|
187
|
+
export class AuthService {
|
|
188
|
+
private auth = createAuthClient({
|
|
189
|
+
clientId: 'your-client-id',
|
|
190
|
+
redirectUri: `${window.location.origin}/callback`,
|
|
191
|
+
});
|
|
214
192
|
|
|
215
|
-
|
|
216
|
-
|
|
193
|
+
private facade = createAngularAuthFacade(this.auth);
|
|
194
|
+
state$ = this.facade.state$;
|
|
217
195
|
|
|
218
|
-
|
|
196
|
+
login() {
|
|
197
|
+
return this.facade.login();
|
|
198
|
+
}
|
|
219
199
|
|
|
220
|
-
|
|
221
|
-
return
|
|
200
|
+
logout() {
|
|
201
|
+
return this.facade.logout();
|
|
222
202
|
}
|
|
223
|
-
return <button onClick={() => auth.logout()}>Logout</button>;
|
|
224
203
|
}
|
|
225
204
|
```
|
|
226
205
|
|
|
227
|
-
|
|
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
|
|
228
218
|
|
|
229
219
|
```ts
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
setCookie: async (name, value) => cookieApi.set(name, value),
|
|
245
|
-
removeCookie: async (name) => cookieApi.delete(name),
|
|
246
|
-
}),
|
|
247
|
-
onRedirect: (url) => { redirect(url); },
|
|
248
|
-
});
|
|
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;
|
|
249
234
|
}
|
|
250
235
|
```
|
|
251
236
|
|
|
252
|
-
##
|
|
237
|
+
## Storage strategy
|
|
253
238
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
- **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 |
|
|
261
245
|
|
|
262
|
-
##
|
|
246
|
+
## Security notes
|
|
263
247
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
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`).
|
|
270
255
|
|
|
271
|
-
|
|
256
|
+
Full policy and reporting process: [SECURITY.md](./SECURITY.md).
|
|
272
257
|
|
|
273
|
-
|
|
274
|
-
- **Missing code_verifier:** The `nuria:oauth:code_verifier` key was not found in storage. Ensure `startLogin()` was called before `handleRedirectCallback()`.
|
|
275
|
-
- **Token refresh fails:** Ensure `enableRefreshToken: true` is set and the auth server supports the `refresh_token` grant for your client.
|
|
258
|
+
## Public API
|
|
276
259
|
|
|
277
|
-
|
|
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
|
+
```
|
|
278
278
|
|
|
279
|
-
|
|
279
|
+
## CI and publish
|
|
280
280
|
|
|
281
|
-
-
|
|
282
|
-
-
|
|
281
|
+
- PR/main runs: typecheck, lint, test, build
|
|
282
|
+
- Tag `v*` runs publish workflow with Trusted Publishing
|
|
283
283
|
|
|
284
|
-
|
|
284
|
+
Publish flow:
|
|
285
285
|
|
|
286
286
|
1. Update `version` in `package.json`
|
|
287
|
-
2.
|
|
288
|
-
3.
|
|
289
|
-
|
|
290
|
-
> **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`.
|
|
291
|
-
|
|
292
|
-
## Endpoint defaults
|
|
293
|
-
|
|
294
|
-
By default, the SDK assumes `ms-auth` OAuth endpoints:
|
|
295
|
-
|
|
296
|
-
- `baseUrl`: `https://ms-auth-v2.nuria.com.br`
|
|
297
|
-
- `authorizationEndpoint`: `${baseUrl}/v2/oauth/authorize`
|
|
298
|
-
- `tokenEndpoint`: `${baseUrl}/v2/oauth/token`
|
|
299
|
-
|
|
300
|
-
You can still override `authorizationEndpoint` and `tokenEndpoint` explicitly when needed.
|
|
301
|
-
|
|
302
|
-
## SSO strategy for multiple portals/apps
|
|
303
|
-
|
|
304
|
-
- Storage adapter (memory/session/local) is **per origin** and does not share tokens across different domains.
|
|
305
|
-
- For real SSO between different portals/apps, rely on auth-server session + `HttpOnly` refresh cookie (`__Host-nuria_rt`) and keep token calls with `credentials: 'include'`.
|
|
306
|
-
- Keep `MemoryStorageAdapter` as default to reduce token exposure in JS.
|
|
287
|
+
2. Tag and push (`git tag vX.Y.Z && git push --tags`)
|
|
288
|
+
3. Workflow validates and publishes
|
|
307
289
|
|
|
308
290
|
## License
|
|
309
291
|
|
|
310
|
-
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 };
|