@ghostly-solutions/auth 0.1.0 → 0.2.1
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 +68 -84
- 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 +16 -0
- package/dist/extension.js +502 -0
- package/dist/extension.js.map +1 -0
- package/dist/index.d.ts +12 -19
- package/dist/index.js +105 -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 +65 -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 +46 -81
- package/docs/overview.md +24 -30
- package/package.json +11 -4
- package/react-client.js +3 -0
|
@@ -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,37 @@ import { ensureClientAuthenticated } from "@ghostly-solutions/auth/next";
|
|
|
103
71
|
await ensureClientAuthenticated();
|
|
104
72
|
```
|
|
105
73
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
```ts
|
|
109
|
-
import { getNextServerSession } from "@ghostly-solutions/auth/next";
|
|
110
|
-
|
|
111
|
-
const session = await getNextServerSession();
|
|
112
|
-
```
|
|
74
|
+
If session is missing, helper triggers `login()` and redirects browser.
|
|
113
75
|
|
|
114
|
-
|
|
76
|
+
## 6. Extension Integration
|
|
115
77
|
|
|
116
78
|
```ts
|
|
117
|
-
import {
|
|
79
|
+
import { createExtensionAuthClient } from "@ghostly-solutions/auth/extension";
|
|
118
80
|
|
|
119
|
-
const
|
|
120
|
-
|
|
81
|
+
const auth = createExtensionAuthClient({
|
|
82
|
+
apiOrigin: "https://api.ghostlysolutions.com",
|
|
83
|
+
launchWebAuthFlow: async ({ authorizeUrl }) => {
|
|
84
|
+
await launchWebAuthFlow(authorizeUrl);
|
|
85
|
+
},
|
|
121
86
|
});
|
|
122
87
|
|
|
123
|
-
|
|
88
|
+
await auth.loginWithWebAuthFlow({
|
|
89
|
+
returnTo: "/",
|
|
90
|
+
});
|
|
124
91
|
```
|
|
125
92
|
|
|
126
|
-
|
|
93
|
+
After auth window flow finishes, SDK executes `POST /oauth/refresh` and updates local session state.
|
|
94
|
+
|
|
95
|
+
## 7. Error Handling
|
|
127
96
|
|
|
128
97
|
```ts
|
|
129
|
-
import { AuthSdkError } from "@ghostly-solutions/auth";
|
|
98
|
+
import { AuthSdkError, authErrorCode } from "@ghostly-solutions/auth";
|
|
130
99
|
|
|
131
100
|
try {
|
|
132
|
-
await
|
|
101
|
+
await auth.requireSession();
|
|
133
102
|
} catch (error) {
|
|
134
|
-
if (error instanceof AuthSdkError) {
|
|
135
|
-
|
|
136
|
-
// show explicit re-login state
|
|
137
|
-
}
|
|
103
|
+
if (error instanceof AuthSdkError && error.code === authErrorCode.unauthorized) {
|
|
104
|
+
auth.login();
|
|
138
105
|
}
|
|
139
|
-
|
|
140
|
-
throw error;
|
|
141
106
|
}
|
|
142
107
|
```
|
package/docs/overview.md
CHANGED
|
@@ -1,41 +1,35 @@
|
|
|
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.
|
|
35
|
-
|
|
36
|
-
## What The SDK Does Not Do
|
|
37
|
-
|
|
38
|
-
- No fallback endpoint discovery.
|
|
39
|
-
- No dynamic base URL/runtime host config.
|
|
40
|
-
- No localStorage token persistence.
|
|
41
|
-
- No background polling refresh loop.
|
|
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`)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ghostly-solutions/auth",
|
|
3
|
-
"version": "0.1
|
|
4
|
-
"description": "Authentication SDK for Ghostly Solutions products using
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "Authentication SDK for Ghostly Solutions products using OAuth redirect and Ghostly Auth API session validation.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
7
7
|
"packageManager": "npm@11.6.3",
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
"sideEffects": false,
|
|
13
13
|
"files": [
|
|
14
14
|
"dist",
|
|
15
|
+
"react-client.js",
|
|
15
16
|
"README.md",
|
|
16
17
|
"docs"
|
|
17
18
|
],
|
|
@@ -26,13 +27,19 @@
|
|
|
26
27
|
},
|
|
27
28
|
"./react": {
|
|
28
29
|
"types": "./dist/react.d.ts",
|
|
29
|
-
"
|
|
30
|
-
"
|
|
30
|
+
"react-server": "./react-client.js",
|
|
31
|
+
"import": "./react-client.js",
|
|
32
|
+
"default": "./react-client.js"
|
|
31
33
|
},
|
|
32
34
|
"./next": {
|
|
33
35
|
"types": "./dist/next.d.ts",
|
|
34
36
|
"import": "./dist/next.js",
|
|
35
37
|
"default": "./dist/next.js"
|
|
38
|
+
},
|
|
39
|
+
"./extension": {
|
|
40
|
+
"types": "./dist/extension.d.ts",
|
|
41
|
+
"import": "./dist/extension.js",
|
|
42
|
+
"default": "./dist/extension.js"
|
|
36
43
|
}
|
|
37
44
|
},
|
|
38
45
|
"publishConfig": {
|
package/react-client.js
ADDED