@ghostly-solutions/auth 0.1.1 → 0.2.2
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 +13 -0
- package/README.md +200 -71
- package/dist/{auth-client-CAHMjodm.d.ts → auth-client-Cdkp07ii.d.ts} +14 -6
- package/dist/{auth-sdk-error-DKM7PyKC.d.ts → auth-sdk-error-D3gsfK9d.d.ts} +0 -3
- package/dist/extension.d.ts +49 -0
- package/dist/extension.js +634 -0
- package/dist/extension.js.map +1 -0
- package/dist/index.d.ts +13 -19
- package/dist/index.js +106 -119
- package/dist/index.js.map +1 -1
- package/dist/next.d.ts +4 -27
- package/dist/next.js +125 -383
- package/dist/next.js.map +1 -1
- package/dist/react.d.ts +4 -20
- package/dist/react.js +129 -176
- package/dist/react.js.map +1 -1
- package/docs/api-reference.md +66 -89
- package/docs/architecture.md +28 -46
- package/docs/development-and-ci.md +15 -19
- package/docs/index.md +1 -15
- package/docs/integration-guide.md +54 -80
- package/docs/overview.md +27 -28
- package/package.json +15 -2
|
@@ -6,96 +6,64 @@
|
|
|
6
6
|
npm install @ghostly-solutions/auth
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
-
## 2.
|
|
9
|
+
## 2. Browser App
|
|
10
10
|
|
|
11
11
|
```ts
|
|
12
12
|
import { createAuthClient } from "@ghostly-solutions/auth";
|
|
13
13
|
|
|
14
|
-
const
|
|
14
|
+
const auth = createAuthClient({ application: "admin" });
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
authClient.login();
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export async function loadCurrentSession() {
|
|
21
|
-
return authClient.getSession();
|
|
22
|
-
}
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
## 3. Callback Route (`/auth/callback`)
|
|
26
|
-
|
|
27
|
-
```ts
|
|
28
|
-
import { createAuthClient } from "@ghostly-solutions/auth";
|
|
29
|
-
|
|
30
|
-
const authClient = createAuthClient();
|
|
16
|
+
await auth.init();
|
|
31
17
|
|
|
32
|
-
await
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
```tsx
|
|
38
|
-
import { AuthCallbackHandler } from "@ghostly-solutions/auth/react";
|
|
39
|
-
|
|
40
|
-
export default function AuthCallbackPage() {
|
|
41
|
-
return (
|
|
42
|
-
<AuthCallbackHandler
|
|
43
|
-
processing={<div>Signing in...</div>}
|
|
44
|
-
renderError={() => <div>Sign in failed. Please retry.</div>}
|
|
45
|
-
/>
|
|
46
|
-
);
|
|
18
|
+
const session = await auth.getSession();
|
|
19
|
+
if (!session) {
|
|
20
|
+
auth.login({
|
|
21
|
+
returnTo: "/",
|
|
22
|
+
});
|
|
47
23
|
}
|
|
48
24
|
```
|
|
49
25
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
```ts
|
|
53
|
-
const result = await authClient.processCallback();
|
|
54
|
-
window.location.replace(result.redirectTo);
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
## 4. React Integration
|
|
26
|
+
## 3. React App
|
|
58
27
|
|
|
59
28
|
```tsx
|
|
60
29
|
import { AuthProvider, AuthSessionGate } from "@ghostly-solutions/auth/react";
|
|
30
|
+
import type { GhostlySession } from "@ghostly-solutions/auth";
|
|
61
31
|
|
|
62
|
-
function
|
|
32
|
+
function Root({ initialSession }: { initialSession?: GhostlySession | null }) {
|
|
63
33
|
return (
|
|
64
|
-
<
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export function AppRoot() {
|
|
73
|
-
return (
|
|
74
|
-
<AuthProvider>
|
|
75
|
-
<SessionGate />
|
|
34
|
+
<AuthProvider initialSession={initialSession}>
|
|
35
|
+
<AuthSessionGate
|
|
36
|
+
loading={<div>Loading...</div>}
|
|
37
|
+
unauthorized={({ login }) => <button onClick={() => login()}>Sign in</button>}
|
|
38
|
+
authorized={(session) => <div>{session.username}</div>}
|
|
39
|
+
/>
|
|
76
40
|
</AuthProvider>
|
|
77
41
|
);
|
|
78
42
|
}
|
|
79
43
|
```
|
|
80
44
|
|
|
81
|
-
|
|
45
|
+
When `initialSession` is passed from server rendering, React UI starts in a resolved auth state without guest/auth flicker.
|
|
46
|
+
|
|
47
|
+
## 4. Next.js App Router (Server)
|
|
82
48
|
|
|
83
49
|
```ts
|
|
84
|
-
import {
|
|
50
|
+
import { requireNextServerSession } from "@ghostly-solutions/auth/next";
|
|
85
51
|
|
|
86
|
-
export async function
|
|
87
|
-
const session = await
|
|
88
|
-
headers
|
|
52
|
+
export async function readProtectedData(headers: Headers) {
|
|
53
|
+
const session = await requireNextServerSession({
|
|
54
|
+
headers,
|
|
55
|
+
apiOrigin: "https://api.ghostlysolutions.com",
|
|
89
56
|
});
|
|
90
57
|
|
|
91
58
|
return {
|
|
92
59
|
actorId: session.id,
|
|
93
|
-
actorRole: session.role,
|
|
94
60
|
};
|
|
95
61
|
}
|
|
96
62
|
```
|
|
97
63
|
|
|
98
|
-
|
|
64
|
+
No route handlers are required from the application.
|
|
65
|
+
|
|
66
|
+
## 5. Next/React Client Guard
|
|
99
67
|
|
|
100
68
|
```ts
|
|
101
69
|
import { ensureClientAuthenticated } from "@ghostly-solutions/auth/next";
|
|
@@ -103,40 +71,46 @@ import { ensureClientAuthenticated } from "@ghostly-solutions/auth/next";
|
|
|
103
71
|
await ensureClientAuthenticated();
|
|
104
72
|
```
|
|
105
73
|
|
|
106
|
-
|
|
74
|
+
If session is missing, helper triggers `login()` and redirects browser.
|
|
75
|
+
|
|
76
|
+
## 6. Extension Integration
|
|
107
77
|
|
|
108
78
|
```ts
|
|
109
|
-
import {
|
|
79
|
+
import { createChromeExtensionAuthClient } from "@ghostly-solutions/auth/extension";
|
|
80
|
+
|
|
81
|
+
const auth = createChromeExtensionAuthClient({
|
|
82
|
+
apiOrigin: "https://auth.ghostlysolutions.com",
|
|
83
|
+
application: "extension",
|
|
84
|
+
});
|
|
110
85
|
|
|
111
|
-
const session = await
|
|
86
|
+
const session = await auth.getSession({ forceRefresh: true });
|
|
87
|
+
if (!session) {
|
|
88
|
+
await auth.loginWithTabFlow({
|
|
89
|
+
returnTo: "/",
|
|
90
|
+
});
|
|
91
|
+
}
|
|
112
92
|
```
|
|
113
93
|
|
|
114
|
-
|
|
94
|
+
`createChromeExtensionAuthClient()` is the preferred extension entrypoint. It manages:
|
|
115
95
|
|
|
116
|
-
|
|
117
|
-
|
|
96
|
+
- `chrome.tabs.create(...)` for login tab flow
|
|
97
|
+
- `chrome.cookies` access for `gs_auth_session`
|
|
98
|
+
- `chrome.storage.local` persistence for exchanged bearer token and cached session
|
|
118
99
|
|
|
119
|
-
|
|
120
|
-
mode: "mock",
|
|
121
|
-
});
|
|
100
|
+
`loginWithTabFlow()` opens the authorize URL in a regular browser tab and relies on later `init()` / `getSession()` calls to observe the server-owned session.
|
|
122
101
|
|
|
123
|
-
|
|
124
|
-
```
|
|
102
|
+
`loginWithWebAuthFlow()` remains available through the lower-level `createExtensionAuthClient()` for `chrome.identity.launchWebAuthFlow(...)` integrations.
|
|
125
103
|
|
|
126
|
-
##
|
|
104
|
+
## 7. Error Handling
|
|
127
105
|
|
|
128
106
|
```ts
|
|
129
|
-
import { AuthSdkError } from "@ghostly-solutions/auth";
|
|
107
|
+
import { AuthSdkError, authErrorCode } from "@ghostly-solutions/auth";
|
|
130
108
|
|
|
131
109
|
try {
|
|
132
|
-
await
|
|
110
|
+
await auth.requireSession();
|
|
133
111
|
} catch (error) {
|
|
134
|
-
if (error instanceof AuthSdkError) {
|
|
135
|
-
|
|
136
|
-
// show explicit re-login state
|
|
137
|
-
}
|
|
112
|
+
if (error instanceof AuthSdkError && error.code === authErrorCode.unauthorized) {
|
|
113
|
+
auth.login();
|
|
138
114
|
}
|
|
139
|
-
|
|
140
|
-
throw error;
|
|
141
115
|
}
|
|
142
116
|
```
|
package/docs/overview.md
CHANGED
|
@@ -1,41 +1,40 @@
|
|
|
1
1
|
# Overview
|
|
2
2
|
|
|
3
|
-
`@ghostly-solutions/auth` is a
|
|
3
|
+
`@ghostly-solutions/auth` is a product SDK for Ghostly OAuth authentication.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Goals
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
7
|
+
- One fixed browser flow for all products.
|
|
8
|
+
- Cookie-only session in browser (`HttpOnly`, server-owned).
|
|
9
|
+
- No callback token parsing in frontend code.
|
|
10
|
+
- No app-side auth route handlers.
|
|
11
11
|
|
|
12
|
-
##
|
|
12
|
+
## OAuth Flow
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
1. `auth.login()` redirects browser to `GET /oauth/authorize?return_to=...`, and may include logical `app=...`.
|
|
15
|
+
2. OAuth provider completes login and returns to `GET /oauth/callback/provider`.
|
|
16
|
+
3. API validates provider response, creates session, sets cookie.
|
|
17
|
+
4. API redirects browser back to `return_to`.
|
|
18
|
+
5. SDK calls `GET /oauth/session` during `init()` and exposes typed session state.
|
|
15
19
|
|
|
16
|
-
|
|
17
|
-
- `POST /v1/auth/keycloak/validate`
|
|
18
|
-
- `GET /v1/auth/me`
|
|
19
|
-
- `POST /v1/auth/logout`
|
|
20
|
+
## Fixed API Endpoints
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
- `GET /oauth/authorize`
|
|
23
|
+
- `GET /oauth/callback/provider`
|
|
24
|
+
- `GET /oauth/session`
|
|
25
|
+
- `POST /oauth/refresh`
|
|
26
|
+
- `POST /oauth/logout`
|
|
22
27
|
|
|
23
|
-
|
|
24
|
-
- Callback JWT query param: `token`
|
|
28
|
+
The SDK can optionally identify the logical application, for example `admin` or `extension`, but it must not expose raw IdP client configuration such as Keycloak `client_id`.
|
|
25
29
|
|
|
26
|
-
##
|
|
30
|
+
## Runtime Scope
|
|
27
31
|
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
-
|
|
31
|
-
-
|
|
32
|
-
- In-memory session state and subscription API.
|
|
33
|
-
- Cross-tab synchronization via `BroadcastChannel`.
|
|
34
|
-
- React state adapter and Next.js guard helpers.
|
|
32
|
+
- Core browser SDK (`@ghostly-solutions/auth`)
|
|
33
|
+
- React adapter (`@ghostly-solutions/auth/react`)
|
|
34
|
+
- Next server helpers (`@ghostly-solutions/auth/next`)
|
|
35
|
+
- Browser-extension adapter (`@ghostly-solutions/auth/extension`)
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
The extension adapter supports two orchestration modes:
|
|
37
38
|
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
- No localStorage token persistence.
|
|
41
|
-
- No background polling refresh loop.
|
|
39
|
+
- tab flow: open `/oauth/authorize` in a regular browser tab
|
|
40
|
+
- web auth flow: wrap `chrome.identity.launchWebAuthFlow(...)`
|
package/package.json
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ghostly-solutions/auth",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Authentication SDK for Ghostly Solutions products using
|
|
3
|
+
"version": "0.2.2",
|
|
4
|
+
"description": "Authentication SDK for Ghostly Solutions products using OAuth redirect and Ghostly Auth API session validation.",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://gitlab.ghostlysolutions.com/ghostlysolutions/shared/sdk/auth-client.git"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://gitlab.ghostlysolutions.com/ghostlysolutions/shared/sdk/auth-client",
|
|
10
|
+
"author": "Ghostly Solutions",
|
|
11
|
+
"license": "UNLICENSED",
|
|
5
12
|
"type": "module",
|
|
6
13
|
"private": false,
|
|
7
14
|
"packageManager": "npm@11.6.3",
|
|
@@ -27,6 +34,7 @@
|
|
|
27
34
|
},
|
|
28
35
|
"./react": {
|
|
29
36
|
"types": "./dist/react.d.ts",
|
|
37
|
+
"react-server": "./react-client.js",
|
|
30
38
|
"import": "./react-client.js",
|
|
31
39
|
"default": "./react-client.js"
|
|
32
40
|
},
|
|
@@ -34,6 +42,11 @@
|
|
|
34
42
|
"types": "./dist/next.d.ts",
|
|
35
43
|
"import": "./dist/next.js",
|
|
36
44
|
"default": "./dist/next.js"
|
|
45
|
+
},
|
|
46
|
+
"./extension": {
|
|
47
|
+
"types": "./dist/extension.d.ts",
|
|
48
|
+
"import": "./dist/extension.js",
|
|
49
|
+
"default": "./dist/extension.js"
|
|
37
50
|
}
|
|
38
51
|
},
|
|
39
52
|
"publishConfig": {
|