@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.
@@ -6,96 +6,64 @@
6
6
  npm install @ghostly-solutions/auth
7
7
  ```
8
8
 
9
- ## 2. Core Browser Integration
9
+ ## 2. Browser App
10
10
 
11
11
  ```ts
12
12
  import { createAuthClient } from "@ghostly-solutions/auth";
13
13
 
14
- const authClient = createAuthClient();
14
+ const auth = createAuthClient({ application: "admin" });
15
15
 
16
- export function onLoginClick(): void {
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 authClient.completeCallbackRedirect();
33
- ```
34
-
35
- Or use the ready React helper:
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
- Alternative when app controls redirect manually:
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 SessionGate() {
32
+ function Root({ initialSession }: { initialSession?: GhostlySession | null }) {
63
33
  return (
64
- <AuthSessionGate
65
- loading={<div>Loading...</div>}
66
- unauthorized={({ login }) => <button onClick={() => login()}>Sign in</button>}
67
- authorized={(session) => <div>{session.email}</div>}
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
- ## 5. Next.js App Router Integration
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 { requireServerSession } from "@ghostly-solutions/auth/next";
50
+ import { requireNextServerSession } from "@ghostly-solutions/auth/next";
85
51
 
86
- export async function loadProtectedData(requestHeaders: Headers) {
87
- const session = await requireServerSession({
88
- headers: requestHeaders,
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
- Client-side guard:
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
- No-glue server helper:
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
- Ready route handlers (mock/proxy):
76
+ ## 6. Extension Integration
115
77
 
116
78
  ```ts
117
- import { createNextAuthRouteHandlers } from "@ghostly-solutions/auth/next";
79
+ import { createExtensionAuthClient } from "@ghostly-solutions/auth/extension";
118
80
 
119
- const authHandlers = createNextAuthRouteHandlers({
120
- mode: "mock",
81
+ const auth = createExtensionAuthClient({
82
+ apiOrigin: "https://api.ghostlysolutions.com",
83
+ launchWebAuthFlow: async ({ authorizeUrl }) => {
84
+ await launchWebAuthFlow(authorizeUrl);
85
+ },
121
86
  });
122
87
 
123
- export const GET = authHandlers.keycloakLoginGet;
88
+ await auth.loginWithWebAuthFlow({
89
+ returnTo: "/",
90
+ });
124
91
  ```
125
92
 
126
- ## 6. Error Handling Pattern
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 authClient.processCallback();
101
+ await auth.requireSession();
133
102
  } catch (error) {
134
- if (error instanceof AuthSdkError) {
135
- if (error.code === "callback_invalid_token") {
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 focused authentication SDK for Ghostly Solutions products.
3
+ `@ghostly-solutions/auth` is a product SDK for Ghostly OAuth authentication.
4
4
 
5
- ## Design Goals
5
+ ## Goals
6
6
 
7
- - Keep authentication behavior deterministic and product-wide consistent.
8
- - Expose strict TypeScript contracts for sessions and errors.
9
- - Work in browser apps, React apps, and Next.js App Router environments.
10
- - Keep API shape minimal: login, callback processing, session, logout, and guards.
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
- ## Fixed External Contract
12
+ ## OAuth Flow
13
13
 
14
- The SDK uses fixed API routes and does not accept runtime endpoint configuration:
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
- - `GET /v1/auth/keycloak/login`
17
- - `POST /v1/auth/keycloak/validate`
18
- - `GET /v1/auth/me`
19
- - `POST /v1/auth/logout`
20
+ ## Fixed API Endpoints
20
21
 
21
- Callback route and token input are fixed:
22
+ - `GET /oauth/authorize`
23
+ - `GET /oauth/callback/provider`
24
+ - `GET /oauth/session`
25
+ - `POST /oauth/refresh`
26
+ - `POST /oauth/logout`
22
27
 
23
- - Callback path: `/auth/callback`
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
- ## What The SDK Handles
30
+ ## Runtime Scope
27
31
 
28
- - Redirect start to login endpoint.
29
- - Callback token extraction and immediate URL cleanup.
30
- - JWT validation request through Ghostly API.
31
- - Session fetch and lazy revalidation.
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.0",
4
- "description": "Authentication SDK for Ghostly Solutions products using Keycloak redirect and Ghostly Auth API session validation.",
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
- "import": "./dist/react.js",
30
- "default": "./dist/react.js"
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": {
@@ -0,0 +1,3 @@
1
+ "use client";
2
+
3
+ export * from "./dist/react.js";