@authgate/browser 0.1.0 → 0.3.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/LICENSE +21 -0
- package/README.md +128 -21
- package/dist/index.d.ts +61 -1
- package/dist/index.js +114 -8
- package/package.json +5 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Alexander Lupatsiy
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -2,18 +2,33 @@
|
|
|
2
2
|
|
|
3
3
|
Minimal browser-side helpers for applications using **AuthGate**.
|
|
4
4
|
|
|
5
|
-
This package provides
|
|
6
|
-
with an AuthGate-backed authentication
|
|
7
|
-
|
|
5
|
+
This package provides **explicit, framework-agnostic primitives** for integrating
|
|
6
|
+
browser-based UIs (SSR or SPA) with an AuthGate-backed authentication system.
|
|
7
|
+
|
|
8
|
+
It intentionally avoids hidden behavior, background state mutation, or
|
|
9
|
+
framework-specific abstractions.
|
|
8
10
|
|
|
9
11
|
---
|
|
10
12
|
|
|
11
|
-
##
|
|
13
|
+
## Design goals
|
|
12
14
|
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
+
- Explicit behavior (no magic, no background auth)
|
|
16
|
+
- Browser-only responsibility (cookies, CSRF, refresh)
|
|
17
|
+
- Framework-agnostic (React, Vue, Svelte, vanilla JS)
|
|
18
|
+
- Composable primitives + optional convenience helpers
|
|
15
19
|
- Zero dependencies
|
|
16
|
-
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Features
|
|
24
|
+
|
|
25
|
+
- Read AuthGate CSRF token from browser cookies
|
|
26
|
+
- Perform a CSRF-protected logout request
|
|
27
|
+
- Explicit browser-side session refresh
|
|
28
|
+
- Optional `fetch` wrapper with **single-retry refresh semantics**
|
|
29
|
+
- Clear success / failure signaling
|
|
30
|
+
- Optional redirect on logout
|
|
31
|
+
- No runtime dependencies
|
|
17
32
|
|
|
18
33
|
---
|
|
19
34
|
|
|
@@ -37,6 +52,9 @@ const csrf = getCSRFToken();
|
|
|
37
52
|
|
|
38
53
|
Returns the value of the `authgate_csrf` cookie, or `null` if not present.
|
|
39
54
|
|
|
55
|
+
This function only **reads** the CSRF token.
|
|
56
|
+
It does not generate or validate it.
|
|
57
|
+
|
|
40
58
|
---
|
|
41
59
|
|
|
42
60
|
### Logout
|
|
@@ -44,7 +62,7 @@ Returns the value of the `authgate_csrf` cookie, or `null` if not present.
|
|
|
44
62
|
```ts
|
|
45
63
|
import { logout } from "@authgate/browser";
|
|
46
64
|
|
|
47
|
-
await logout();
|
|
65
|
+
const result = await logout();
|
|
48
66
|
```
|
|
49
67
|
|
|
50
68
|
This will:
|
|
@@ -53,6 +71,17 @@ This will:
|
|
|
53
71
|
- Attach the CSRF token via `X-CSRF-Token`
|
|
54
72
|
- Include credentials (`cookies`)
|
|
55
73
|
|
|
74
|
+
The function returns an explicit result:
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
type LogoutResult =
|
|
78
|
+
| { ok: true }
|
|
79
|
+
| { ok: false; reason: "missing_csrf" | "request_failed" | "unauthorized" };
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Applications that do not need to react programmatically may safely ignore the
|
|
83
|
+
return value.
|
|
84
|
+
|
|
56
85
|
---
|
|
57
86
|
|
|
58
87
|
### Logout with redirect
|
|
@@ -61,41 +90,119 @@ This will:
|
|
|
61
90
|
await logout({ redirectTo: "/" });
|
|
62
91
|
```
|
|
63
92
|
|
|
64
|
-
|
|
93
|
+
If the logout request succeeds, the browser is redirected to the given path.
|
|
94
|
+
|
|
95
|
+
Redirecting is an optional side-effect and does **not** define success.
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Session refresh (explicit)
|
|
100
|
+
|
|
101
|
+
### `refreshSession`
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
import { refreshSession } from "@authgate/browser";
|
|
105
|
+
|
|
106
|
+
const refreshed = await refreshSession();
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Attempts to refresh the current AuthGate session by calling:
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
POST /auth/refresh
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Behavior:
|
|
116
|
+
|
|
117
|
+
- Returns `true` if refresh succeeded
|
|
118
|
+
- Returns `false` if refresh failed for any reason
|
|
119
|
+
|
|
120
|
+
This function:
|
|
121
|
+
|
|
122
|
+
- does **not** retry
|
|
123
|
+
- does **not** redirect
|
|
124
|
+
- does **not** throw
|
|
125
|
+
- does **not** modify application state
|
|
126
|
+
|
|
127
|
+
It is intended for applications that want **manual control** over refresh logic.
|
|
65
128
|
|
|
66
129
|
---
|
|
67
130
|
|
|
68
|
-
##
|
|
131
|
+
## Fetch wrapper (optional convenience)
|
|
132
|
+
|
|
133
|
+
### `authFetch`
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
import { authFetch } from "@authgate/browser";
|
|
137
|
+
|
|
138
|
+
const res = await authFetch("/api/data");
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
`authFetch` is an **optional convenience wrapper** around `fetch` with
|
|
142
|
+
AuthGate-aware refresh behavior.
|
|
143
|
+
|
|
144
|
+
Behavior:
|
|
145
|
+
|
|
146
|
+
1. Performs the request with credentials
|
|
147
|
+
2. If the response is **not `401`**, returns it directly
|
|
148
|
+
3. If the response **is `401`**:
|
|
149
|
+
- Attempts `refreshSession()`
|
|
150
|
+
- If refresh succeeds, retries the original request **once**
|
|
151
|
+
- Otherwise, returns the original `401` response
|
|
152
|
+
|
|
153
|
+
Important properties:
|
|
154
|
+
|
|
155
|
+
- At most **one retry**
|
|
156
|
+
- No redirects
|
|
157
|
+
- No background refresh
|
|
158
|
+
- No swallowed failures
|
|
159
|
+
|
|
160
|
+
Applications remain fully in control of UX decisions.
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Example (React / SPA)
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
const res = await authFetch("/api/me");
|
|
168
|
+
|
|
169
|
+
if (res.status === 401) {
|
|
170
|
+
setUser(null);
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Security model
|
|
69
177
|
|
|
70
178
|
- CSRF tokens are **not generated** by this package
|
|
71
179
|
- CSRF validation is **enforced by AuthGate**
|
|
72
|
-
-
|
|
180
|
+
- Refresh tokens are **never exposed to JavaScript**
|
|
181
|
+
- All authentication state is owned by AuthGate
|
|
73
182
|
|
|
74
|
-
|
|
183
|
+
This package only forwards existing browser state explicitly.
|
|
75
184
|
|
|
76
185
|
---
|
|
77
186
|
|
|
78
|
-
## What
|
|
187
|
+
## What this package does NOT do
|
|
79
188
|
|
|
80
189
|
- No authentication logic
|
|
81
|
-
- No
|
|
190
|
+
- No credential storage
|
|
191
|
+
- No background token refresh
|
|
82
192
|
- No session management
|
|
83
|
-
- No
|
|
193
|
+
- No authorization or role handling
|
|
194
|
+
- No implicit redirects
|
|
84
195
|
- No framework-specific helpers
|
|
85
196
|
|
|
86
|
-
This package exists solely to reduce boilerplate and prevent integration mistakes
|
|
197
|
+
This package exists solely to reduce boilerplate and prevent integration mistakes
|
|
198
|
+
while preserving full application control.
|
|
87
199
|
|
|
88
200
|
---
|
|
89
201
|
|
|
90
202
|
## Compatibility
|
|
91
203
|
|
|
92
204
|
- Works with any backend protected by AuthGate
|
|
93
|
-
- Compatible with SSR and SPA architectures
|
|
94
|
-
- Safe to use in multi-app or monorepo setups
|
|
95
205
|
|
|
96
206
|
---
|
|
97
207
|
|
|
98
208
|
## License
|
|
99
|
-
|
|
100
|
-
MIT
|
|
101
|
-
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns the AuthGate CSRF token from the browser cookies.
|
|
3
|
+
*
|
|
4
|
+
* The CSRF token is issued by AuthGate and stored in the `authgate_csrf`
|
|
5
|
+
* cookie. This helper does not validate the token; it only reads it.
|
|
6
|
+
*
|
|
7
|
+
* @returns The CSRF token string, or `null` if the cookie is missing.
|
|
8
|
+
*/
|
|
1
9
|
export declare function getCSRFToken(): string | null;
|
|
10
|
+
/**
|
|
11
|
+
* The result of a logout attempt.
|
|
12
|
+
*
|
|
13
|
+
* - `{ ok: true }` indicates that logout succeeded.
|
|
14
|
+
* - `{ ok: false, reason }` indicates that logout failed for a known reason.
|
|
15
|
+
*/
|
|
16
|
+
type LogoutResult = {
|
|
17
|
+
ok: true;
|
|
18
|
+
} | {
|
|
19
|
+
ok: false;
|
|
20
|
+
reason: "missing_csrf" | "request_failed" | "unauthorized";
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Logs the user out by calling the AuthGate logout endpoint.
|
|
24
|
+
*
|
|
25
|
+
* This function:
|
|
26
|
+
* - Reads the CSRF token from the browser cookies
|
|
27
|
+
* - Sends a POST request to `/auth/logout`
|
|
28
|
+
* - Optionally redirects the browser on success
|
|
29
|
+
*
|
|
30
|
+
* Redirecting is a side-effect and does not define success. Applications
|
|
31
|
+
* that need to react to logout programmatically (e.g. SPA state updates)
|
|
32
|
+
* should inspect the returned result instead.
|
|
33
|
+
*
|
|
34
|
+
* @param opts.redirectTo Optional URL to redirect to after successful logout.
|
|
35
|
+
* @returns A `LogoutResult` indicating whether logout succeeded or failed.
|
|
36
|
+
*/
|
|
2
37
|
export declare function logout(opts?: {
|
|
3
38
|
redirectTo?: string;
|
|
4
|
-
}): Promise<
|
|
39
|
+
}): Promise<LogoutResult>;
|
|
40
|
+
/**
|
|
41
|
+
* authFetch performs a fetch request with AuthGate-aware refresh-once behavior.
|
|
42
|
+
*
|
|
43
|
+
* Behavior:
|
|
44
|
+
* - Always includes credentials
|
|
45
|
+
* - If the response is NOT 401 - returns it directly
|
|
46
|
+
* - If the response IS 401:
|
|
47
|
+
* - Attempts POST /auth/refresh with CSRF
|
|
48
|
+
* - If refresh succeeds - retries original request ONCE
|
|
49
|
+
* - Otherwise - returns original 401 response
|
|
50
|
+
*/
|
|
51
|
+
export declare function authFetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
|
|
52
|
+
/**
|
|
53
|
+
* refreshSession attempts to refresh the current AuthGate session.
|
|
54
|
+
*
|
|
55
|
+
* It performs:
|
|
56
|
+
* POST /auth/refresh
|
|
57
|
+
* with CSRF protection and credentials
|
|
58
|
+
*
|
|
59
|
+
* The function:
|
|
60
|
+
* - returns true if refresh succeeded
|
|
61
|
+
* - returns false if refresh failed for any reason
|
|
62
|
+
*/
|
|
63
|
+
export declare function refreshSession(): Promise<boolean>;
|
|
64
|
+
export {};
|
package/dist/index.js
CHANGED
|
@@ -1,22 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reads a cookie value by name from `document.cookie`.
|
|
3
|
+
*
|
|
4
|
+
* This is a small internal helper used by the AuthGate browser SDK.
|
|
5
|
+
* It returns `null` if the cookie is not present.
|
|
6
|
+
*/
|
|
1
7
|
function getCookie(name) {
|
|
2
8
|
const match = document.cookie
|
|
3
9
|
.split("; ")
|
|
4
10
|
.find((c) => c.startsWith(name + "="));
|
|
5
11
|
return match ? decodeURIComponent(match.split("=")[1]) : null;
|
|
6
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* Returns the AuthGate CSRF token from the browser cookies.
|
|
15
|
+
*
|
|
16
|
+
* The CSRF token is issued by AuthGate and stored in the `authgate_csrf`
|
|
17
|
+
* cookie. This helper does not validate the token; it only reads it.
|
|
18
|
+
*
|
|
19
|
+
* @returns The CSRF token string, or `null` if the cookie is missing.
|
|
20
|
+
*/
|
|
7
21
|
export function getCSRFToken() {
|
|
8
22
|
return getCookie("authgate_csrf");
|
|
9
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* Logs the user out by calling the AuthGate logout endpoint.
|
|
26
|
+
*
|
|
27
|
+
* This function:
|
|
28
|
+
* - Reads the CSRF token from the browser cookies
|
|
29
|
+
* - Sends a POST request to `/auth/logout`
|
|
30
|
+
* - Optionally redirects the browser on success
|
|
31
|
+
*
|
|
32
|
+
* Redirecting is a side-effect and does not define success. Applications
|
|
33
|
+
* that need to react to logout programmatically (e.g. SPA state updates)
|
|
34
|
+
* should inspect the returned result instead.
|
|
35
|
+
*
|
|
36
|
+
* @param opts.redirectTo Optional URL to redirect to after successful logout.
|
|
37
|
+
* @returns A `LogoutResult` indicating whether logout succeeded or failed.
|
|
38
|
+
*/
|
|
10
39
|
export async function logout(opts) {
|
|
11
40
|
const csrf = getCSRFToken();
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
41
|
+
if (!csrf) {
|
|
42
|
+
return { ok: false, reason: "missing_csrf" };
|
|
43
|
+
}
|
|
44
|
+
let res;
|
|
45
|
+
try {
|
|
46
|
+
res = await fetch("/auth/logout", {
|
|
47
|
+
method: "POST",
|
|
48
|
+
headers: {
|
|
49
|
+
"X-CSRF-Token": csrf,
|
|
50
|
+
},
|
|
51
|
+
credentials: "include",
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return { ok: false, reason: "request_failed" };
|
|
56
|
+
}
|
|
57
|
+
if (!res.ok) {
|
|
58
|
+
return {
|
|
59
|
+
ok: false,
|
|
60
|
+
reason: res.status === 401 || res.status === 403
|
|
61
|
+
? "unauthorized"
|
|
62
|
+
: "request_failed",
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
if (opts?.redirectTo) {
|
|
20
66
|
window.location.href = opts.redirectTo;
|
|
21
67
|
}
|
|
68
|
+
return { ok: true };
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* authFetch performs a fetch request with AuthGate-aware refresh-once behavior.
|
|
72
|
+
*
|
|
73
|
+
* Behavior:
|
|
74
|
+
* - Always includes credentials
|
|
75
|
+
* - If the response is NOT 401 - returns it directly
|
|
76
|
+
* - If the response IS 401:
|
|
77
|
+
* - Attempts POST /auth/refresh with CSRF
|
|
78
|
+
* - If refresh succeeds - retries original request ONCE
|
|
79
|
+
* - Otherwise - returns original 401 response
|
|
80
|
+
*/
|
|
81
|
+
export async function authFetch(input, init = {}) {
|
|
82
|
+
const res = await fetch(input, withCredentials(init));
|
|
83
|
+
if (res.status !== 401) {
|
|
84
|
+
return res;
|
|
85
|
+
}
|
|
86
|
+
const refreshed = await refreshSession();
|
|
87
|
+
if (!refreshed) {
|
|
88
|
+
return res;
|
|
89
|
+
}
|
|
90
|
+
return fetch(input, withCredentials(init));
|
|
91
|
+
}
|
|
92
|
+
function withCredentials(init) {
|
|
93
|
+
return {
|
|
94
|
+
...init,
|
|
95
|
+
credentials: "include",
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* refreshSession attempts to refresh the current AuthGate session.
|
|
100
|
+
*
|
|
101
|
+
* It performs:
|
|
102
|
+
* POST /auth/refresh
|
|
103
|
+
* with CSRF protection and credentials
|
|
104
|
+
*
|
|
105
|
+
* The function:
|
|
106
|
+
* - returns true if refresh succeeded
|
|
107
|
+
* - returns false if refresh failed for any reason
|
|
108
|
+
*/
|
|
109
|
+
export async function refreshSession() {
|
|
110
|
+
const csrf = getCSRFToken();
|
|
111
|
+
if (!csrf) {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
let res;
|
|
115
|
+
try {
|
|
116
|
+
res = await fetch("/auth/refresh", {
|
|
117
|
+
method: "POST",
|
|
118
|
+
headers: {
|
|
119
|
+
"X-CSRF-Token": csrf,
|
|
120
|
+
},
|
|
121
|
+
credentials: "include",
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
return res.ok;
|
|
22
128
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@authgate/browser",
|
|
3
|
-
"
|
|
3
|
+
"repository": {
|
|
4
|
+
"type": "git",
|
|
5
|
+
"url": "https://github.com/alexlup06-authgate/authgate-browser.git"
|
|
6
|
+
},
|
|
7
|
+
"version": "0.3.0",
|
|
4
8
|
"description": "Browser-side helpers for AuthGate (logout, CSRF forwarding)",
|
|
5
9
|
"license": "MIT",
|
|
6
10
|
"type": "module",
|