oauth.do 0.1.15 → 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/LICENSE +21 -0
- package/README.md +245 -9
- package/bin/duckdb-auth +71 -0
- package/dist/cli.js +129 -329
- package/dist/cli.js.map +1 -1
- package/dist/hono.d.ts +124 -0
- package/dist/hono.js +599 -0
- package/dist/hono.js.map +1 -0
- package/dist/index.d.ts +5 -89
- package/dist/index.js +24 -24
- package/dist/index.js.map +1 -1
- package/dist/node.d.ts +2 -1
- package/dist/node.js +108 -73
- package/dist/node.js.map +1 -1
- package/dist/react.d.ts +200 -0
- package/dist/react.js +67 -0
- package/dist/react.js.map +1 -0
- package/dist/session-hono.d.ts +143 -0
- package/dist/session-hono.js +406 -0
- package/dist/session-hono.js.map +1 -0
- package/dist/session.d.ts +83 -0
- package/dist/session.js +114 -0
- package/dist/session.js.map +1 -0
- package/dist/types-export.d.ts +90 -0
- package/dist/types-export.js +3 -0
- package/dist/types-export.js.map +1 -0
- package/package.json +83 -9
package/dist/react.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { WorkOsWidgets, ApiKeys as ApiKeys$1, UsersManagement as UsersManagement$1, UserProfile as UserProfile$1 } from '@workos-inc/widgets';
|
|
2
|
+
import { AuthKitProvider, useAuth as useAuth$1 } from '@workos-inc/authkit-react';
|
|
3
|
+
import React, { createContext, useContext } from 'react';
|
|
4
|
+
|
|
5
|
+
var OAUTH_DO_CONFIG = {
|
|
6
|
+
clientId: "client_01JQYTRXK9ZPD8JPJTKDCRB656",
|
|
7
|
+
apiUrl: "https://apis.do",
|
|
8
|
+
authKitDomain: "login.oauth.do"
|
|
9
|
+
};
|
|
10
|
+
var OAuthDoContext = createContext(OAUTH_DO_CONFIG);
|
|
11
|
+
function useOAuthDoConfig() {
|
|
12
|
+
return useContext(OAuthDoContext);
|
|
13
|
+
}
|
|
14
|
+
function OAuthDoProvider({
|
|
15
|
+
children,
|
|
16
|
+
clientId = OAUTH_DO_CONFIG.clientId,
|
|
17
|
+
apiUrl = OAUTH_DO_CONFIG.apiUrl,
|
|
18
|
+
authKitDomain = OAUTH_DO_CONFIG.authKitDomain
|
|
19
|
+
}) {
|
|
20
|
+
const config = { clientId, apiUrl, authKitDomain };
|
|
21
|
+
return /* @__PURE__ */ React.createElement(OAuthDoContext.Provider, { value: config }, /* @__PURE__ */ React.createElement(AuthKitProvider, { clientId }, /* @__PURE__ */ React.createElement(WorkOsWidgets, null, children)));
|
|
22
|
+
}
|
|
23
|
+
function useAuth() {
|
|
24
|
+
return useAuth$1();
|
|
25
|
+
}
|
|
26
|
+
function ApiKeys({ authToken }) {
|
|
27
|
+
return /* @__PURE__ */ React.createElement(ApiKeys$1, { authToken });
|
|
28
|
+
}
|
|
29
|
+
function UsersManagement({ authToken }) {
|
|
30
|
+
return /* @__PURE__ */ React.createElement(UsersManagement$1, { authToken });
|
|
31
|
+
}
|
|
32
|
+
function UserProfile({ authToken }) {
|
|
33
|
+
return /* @__PURE__ */ React.createElement(UserProfile$1, { authToken });
|
|
34
|
+
}
|
|
35
|
+
function SignInButton({
|
|
36
|
+
children = "Sign In",
|
|
37
|
+
className,
|
|
38
|
+
redirectTo = typeof window !== "undefined" ? window.location.href : "/"
|
|
39
|
+
}) {
|
|
40
|
+
const { authKitDomain, clientId } = useOAuthDoConfig();
|
|
41
|
+
const handleClick = () => {
|
|
42
|
+
const url = new URL(`https://${authKitDomain}`);
|
|
43
|
+
url.searchParams.set("client_id", clientId);
|
|
44
|
+
url.searchParams.set("redirect_uri", redirectTo);
|
|
45
|
+
url.searchParams.set("response_type", "code");
|
|
46
|
+
window.location.href = url.toString();
|
|
47
|
+
};
|
|
48
|
+
return /* @__PURE__ */ React.createElement("button", { onClick: handleClick, className }, children);
|
|
49
|
+
}
|
|
50
|
+
function SignOutButton({
|
|
51
|
+
children = "Sign Out",
|
|
52
|
+
className,
|
|
53
|
+
redirectTo = "/"
|
|
54
|
+
}) {
|
|
55
|
+
const { signOut } = useAuth();
|
|
56
|
+
const handleClick = () => {
|
|
57
|
+
signOut();
|
|
58
|
+
if (redirectTo !== "/" && typeof window !== "undefined") {
|
|
59
|
+
window.location.href = redirectTo;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
return /* @__PURE__ */ React.createElement("button", { onClick: handleClick, className }, children);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export { ApiKeys, OAuthDoProvider, SignInButton, SignOutButton, UserProfile, UsersManagement, useAuth, useOAuthDoConfig };
|
|
66
|
+
//# sourceMappingURL=react.js.map
|
|
67
|
+
//# sourceMappingURL=react.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/react.tsx"],"names":["WorkOSAuthKitProvider","useWorkOSAuth","WorkOSApiKeys","WorkOSUsersManagement","WorkOSUserProfile"],"mappings":"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","file":"react.js","sourcesContent":["/**\n * oauth.do/react - React components for authentication\n *\n * Wraps WorkOS AuthKit widgets with oauth.do configuration.\n * Pre-configured with oauth.do WorkOS client ID.\n *\n * @packageDocumentation\n */\n\n'use client'\n\nimport {\n WorkOsWidgets,\n ApiKeys as WorkOSApiKeys,\n UsersManagement as WorkOSUsersManagement,\n UserProfile as WorkOSUserProfile\n} from '@workos-inc/widgets'\nimport {\n AuthKitProvider as WorkOSAuthKitProvider,\n useAuth as useWorkOSAuth,\n type Impersonator,\n type User,\n type AuthenticationMethod\n} from '@workos-inc/authkit-react'\n\n/**\n * Auth token can be a string or a function that returns a Promise<string>\n */\nexport type AuthToken = string | (() => Promise<string>)\nimport React, { createContext, useContext, type ReactNode } from 'react'\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Auth Types (re-exported from authkit-react context)\n// ═══════════════════════════════════════════════════════════════════════════\n\n/**\n * Options for switching organization\n */\nexport interface SwitchToOrganizationOptions {\n organizationId: string\n signInOpts?: {\n screenHint?: 'sign-in' | 'sign-up'\n loginHint?: string\n }\n}\n\n/**\n * Auth state and methods returned by useAuth hook\n */\nexport interface AuthState {\n isLoading: boolean\n user: User | null\n role: string | null\n roles: string[] | null\n organizationId: string | null\n permissions: string[]\n featureFlags: string[]\n impersonator: Impersonator | null\n authenticationMethod: AuthenticationMethod | null\n signIn: () => void\n signUp: () => void\n getUser: () => User | null\n getAccessToken: () => Promise<string>\n signOut: () => void\n switchToOrganization: (options: SwitchToOrganizationOptions) => Promise<void>\n getSignInUrl: () => Promise<string>\n getSignUpUrl: () => Promise<string>\n}\n\n// oauth.do default configuration\nconst OAUTH_DO_CONFIG = {\n clientId: 'client_01JQYTRXK9ZPD8JPJTKDCRB656',\n apiUrl: 'https://apis.do',\n authKitDomain: 'login.oauth.do',\n}\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Context\n// ═══════════════════════════════════════════════════════════════════════════\n\ninterface OAuthDoContextValue {\n clientId: string\n apiUrl: string\n authKitDomain: string\n}\n\nconst OAuthDoContext = createContext<OAuthDoContextValue>(OAUTH_DO_CONFIG)\n\nexport function useOAuthDoConfig(): OAuthDoContextValue {\n return useContext(OAuthDoContext)\n}\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Provider\n// ═══════════════════════════════════════════════════════════════════════════\n\nexport interface OAuthDoProviderProps {\n children: ReactNode\n /** Override the default client ID */\n clientId?: string\n /** Override the API URL */\n apiUrl?: string\n /** Override the AuthKit domain */\n authKitDomain?: string\n}\n\n/**\n * OAuth.do Provider - wraps your app with authentication context\n *\n * @example\n * ```tsx\n * import { OAuthDoProvider } from 'oauth.do/react'\n *\n * export default function App({ children }) {\n * return (\n * <OAuthDoProvider>\n * {children}\n * </OAuthDoProvider>\n * )\n * }\n * ```\n */\nexport function OAuthDoProvider({\n children,\n clientId = OAUTH_DO_CONFIG.clientId,\n apiUrl = OAUTH_DO_CONFIG.apiUrl,\n authKitDomain = OAUTH_DO_CONFIG.authKitDomain,\n}: OAuthDoProviderProps): JSX.Element {\n const config = { clientId, apiUrl, authKitDomain }\n\n return (\n <OAuthDoContext.Provider value={config}>\n <WorkOSAuthKitProvider clientId={clientId}>\n <WorkOsWidgets>\n {children}\n </WorkOsWidgets>\n </WorkOSAuthKitProvider>\n </OAuthDoContext.Provider>\n )\n}\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Hooks\n// ═══════════════════════════════════════════════════════════════════════════\n\n/**\n * useAuth hook - access current user and auth state\n *\n * @example\n * ```tsx\n * import { useAuth } from 'oauth.do/react'\n *\n * function UserGreeting() {\n * const { user, isLoading } = useAuth()\n *\n * if (isLoading) return <span>Loading...</span>\n * if (!user) return <span>Please sign in</span>\n *\n * return <span>Hello, {user.firstName}!</span>\n * }\n * ```\n */\nexport function useAuth(): AuthState {\n return useWorkOSAuth() as AuthState\n}\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Widgets\n// ═══════════════════════════════════════════════════════════════════════════\n\nexport interface ApiKeysProps {\n /** Auth token for the widget (from useAuth().getAccessToken or server-generated) */\n authToken: AuthToken\n}\n\n/**\n * API Keys Widget - manage API keys for your organization\n *\n * Requires the `widgets:api-keys:manage` permission.\n *\n * @example\n * ```tsx\n * import { ApiKeys, useAuth } from 'oauth.do/react'\n *\n * function ApiKeysPage() {\n * const { user, getAccessToken } = useAuth()\n * if (!user) return <p>Please sign in</p>\n *\n * return <ApiKeys authToken={getAccessToken} />\n * }\n * ```\n */\nexport function ApiKeys({ authToken }: ApiKeysProps): JSX.Element {\n return <WorkOSApiKeys authToken={authToken} />\n}\n\nexport interface UsersManagementProps {\n /** Auth token for the widget */\n authToken: AuthToken\n}\n\n/**\n * Users Management Widget - invite, remove, and manage users\n *\n * @example\n * ```tsx\n * import { UsersManagement, useAuth } from 'oauth.do/react'\n *\n * function UsersPage() {\n * const { user, getAccessToken } = useAuth()\n * if (!user) return <p>Please sign in</p>\n *\n * return <UsersManagement authToken={getAccessToken} />\n * }\n * ```\n */\nexport function UsersManagement({ authToken }: UsersManagementProps): JSX.Element {\n return <WorkOSUsersManagement authToken={authToken} />\n}\n\nexport interface UserProfileProps {\n /** Auth token for the widget */\n authToken: AuthToken\n}\n\n/**\n * User Profile Widget - view and edit user profile\n *\n * @example\n * ```tsx\n * import { UserProfile, useAuth } from 'oauth.do/react'\n *\n * function ProfilePage() {\n * const { user, getAccessToken } = useAuth()\n * if (!user) return <p>Please sign in</p>\n *\n * return <UserProfile authToken={getAccessToken} />\n * }\n * ```\n */\nexport function UserProfile({ authToken }: UserProfileProps): JSX.Element {\n return <WorkOSUserProfile authToken={authToken} />\n}\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Login Components\n// ═══════════════════════════════════════════════════════════════════════════\n\nexport interface SignInButtonProps {\n children?: ReactNode\n className?: string\n redirectTo?: string\n}\n\n/**\n * Sign In Button - redirects to oauth.do login\n *\n * @example\n * ```tsx\n * import { SignInButton } from 'oauth.do/react'\n *\n * function Header() {\n * return <SignInButton>Sign In</SignInButton>\n * }\n * ```\n */\nexport function SignInButton({\n children = 'Sign In',\n className,\n redirectTo = typeof window !== 'undefined' ? window.location.href : '/',\n}: SignInButtonProps): JSX.Element {\n const { authKitDomain, clientId } = useOAuthDoConfig()\n\n const handleClick = () => {\n const url = new URL(`https://${authKitDomain}`)\n url.searchParams.set('client_id', clientId)\n url.searchParams.set('redirect_uri', redirectTo)\n url.searchParams.set('response_type', 'code')\n window.location.href = url.toString()\n }\n\n return (\n <button onClick={handleClick} className={className}>\n {children}\n </button>\n )\n}\n\nexport interface SignOutButtonProps {\n children?: ReactNode\n className?: string\n redirectTo?: string\n}\n\n/**\n * Sign Out Button - clears auth state and optionally redirects\n *\n * @example\n * ```tsx\n * import { SignOutButton } from 'oauth.do/react'\n *\n * function Header() {\n * return <SignOutButton>Sign Out</SignOutButton>\n * }\n * ```\n */\nexport function SignOutButton({\n children = 'Sign Out',\n className,\n redirectTo = '/',\n}: SignOutButtonProps): JSX.Element {\n const { signOut } = useAuth()\n\n const handleClick = () => {\n signOut()\n // Redirect after signOut if a custom redirectTo is provided\n if (redirectTo !== '/' && typeof window !== 'undefined') {\n window.location.href = redirectTo\n }\n }\n\n return (\n <button onClick={handleClick} className={className}>\n {children}\n </button>\n )\n}\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Re-exports\n// ═══════════════════════════════════════════════════════════════════════════\n\nexport type { User, Impersonator, AuthenticationMethod }\n"]}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { Context, Hono, MiddlewareHandler } from 'hono';
|
|
2
|
+
import { SessionData, SessionConfig } from './session.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* oauth.do/session-hono - Hono middleware for cookie-based session auth
|
|
6
|
+
*
|
|
7
|
+
* Provides session-based authentication as an alternative to JWT middleware.
|
|
8
|
+
* Uses AES-GCM encrypted cookies for secure, stateless sessions.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* import { Hono } from 'hono'
|
|
13
|
+
* import { sessionAuth, requireSession, createOAuthRoutes } from 'oauth.do/hono'
|
|
14
|
+
*
|
|
15
|
+
* const app = new Hono()
|
|
16
|
+
* app.use('*', sessionAuth())
|
|
17
|
+
* app.route('/auth', createOAuthRoutes({
|
|
18
|
+
* workosApiKey: env.WORKOS_API_KEY,
|
|
19
|
+
* clientId: env.WORKOS_CLIENT_ID,
|
|
20
|
+
* }))
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* User info extracted from session
|
|
26
|
+
*/
|
|
27
|
+
interface SessionUser {
|
|
28
|
+
id: string;
|
|
29
|
+
email?: string;
|
|
30
|
+
name?: string;
|
|
31
|
+
organizationId?: string;
|
|
32
|
+
}
|
|
33
|
+
declare module 'hono' {
|
|
34
|
+
interface ContextVariableMap {
|
|
35
|
+
session: SessionData | null;
|
|
36
|
+
sessionUser: SessionUser | null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Options for session auth middleware
|
|
41
|
+
*/
|
|
42
|
+
interface SessionAuthOptions {
|
|
43
|
+
/** Session configuration (cookie name, secret, etc.) */
|
|
44
|
+
config?: Partial<SessionConfig>;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Options for creating OAuth routes
|
|
48
|
+
*/
|
|
49
|
+
interface OAuthRoutesOptions {
|
|
50
|
+
/** WorkOS API Key */
|
|
51
|
+
workosApiKey?: string;
|
|
52
|
+
/** WorkOS Client ID */
|
|
53
|
+
clientId?: string;
|
|
54
|
+
/** Session configuration */
|
|
55
|
+
session?: Partial<SessionConfig>;
|
|
56
|
+
/** Base URL for OAuth redirects (derived from request if not set) */
|
|
57
|
+
redirectBaseUrl?: string;
|
|
58
|
+
/** Path prefix for auth routes (default: '') */
|
|
59
|
+
pathPrefix?: string;
|
|
60
|
+
/** Callback after successful login */
|
|
61
|
+
onLogin?: (session: SessionData, c: Context) => void | Promise<void>;
|
|
62
|
+
/** Callback after logout */
|
|
63
|
+
onLogout?: (c: Context) => void | Promise<void>;
|
|
64
|
+
/** Enable debug logging */
|
|
65
|
+
debug?: boolean;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Environment bindings for session auth
|
|
69
|
+
*/
|
|
70
|
+
interface SessionEnv {
|
|
71
|
+
WORKOS_API_KEY?: string;
|
|
72
|
+
WORKOS_CLIENT_ID?: string;
|
|
73
|
+
SESSION_SECRET?: string;
|
|
74
|
+
SESSION_COOKIE_NAME?: string;
|
|
75
|
+
SESSION_COOKIE_MAX_AGE?: string;
|
|
76
|
+
SESSION_COOKIE_SECURE?: string;
|
|
77
|
+
SESSION_COOKIE_SAME_SITE?: string;
|
|
78
|
+
AUTH_REDIRECT_BASE_URL?: string;
|
|
79
|
+
DEBUG?: string;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Set session cookie with encrypted session data
|
|
83
|
+
*/
|
|
84
|
+
declare function setSessionCookie(c: Context, session: SessionData, config?: SessionConfig): Promise<void>;
|
|
85
|
+
/**
|
|
86
|
+
* Clear session cookie
|
|
87
|
+
*/
|
|
88
|
+
declare function clearSessionCookie(c: Context, config?: SessionConfig): void;
|
|
89
|
+
/**
|
|
90
|
+
* Get session from cookie
|
|
91
|
+
*/
|
|
92
|
+
declare function getSessionFromCookie(c: Context, config?: SessionConfig): Promise<SessionData | null>;
|
|
93
|
+
/**
|
|
94
|
+
* Session auth middleware - populates c.var.session and c.var.sessionUser
|
|
95
|
+
*
|
|
96
|
+
* Non-blocking: sets null if no session exists. Use requireSession() for guarded access.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```ts
|
|
100
|
+
* app.use('*', sessionAuth())
|
|
101
|
+
* app.get('/me', (c) => {
|
|
102
|
+
* if (!c.var.session) return c.json({ error: 'Not authenticated' }, 401)
|
|
103
|
+
* return c.json(c.var.sessionUser)
|
|
104
|
+
* })
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
declare function sessionAuth(options?: SessionAuthOptions): MiddlewareHandler;
|
|
108
|
+
/**
|
|
109
|
+
* Require session middleware - returns 401 if no valid session
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```ts
|
|
113
|
+
* app.use('/api/*', requireSession())
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
declare function requireSession(options?: SessionAuthOptions): MiddlewareHandler;
|
|
117
|
+
/**
|
|
118
|
+
* Create mountable OAuth routes for login, callback, logout, me, and refresh.
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```ts
|
|
122
|
+
* import { Hono } from 'hono'
|
|
123
|
+
* import { createOAuthRoutes } from 'oauth.do/hono'
|
|
124
|
+
*
|
|
125
|
+
* const app = new Hono()
|
|
126
|
+
* app.route('/auth', createOAuthRoutes({
|
|
127
|
+
* workosApiKey: env.WORKOS_API_KEY,
|
|
128
|
+
* clientId: env.WORKOS_CLIENT_ID,
|
|
129
|
+
* session: { secret: env.SESSION_SECRET },
|
|
130
|
+
* }))
|
|
131
|
+
* ```
|
|
132
|
+
*
|
|
133
|
+
* Routes:
|
|
134
|
+
* - GET /login - Redirect to WorkOS authorization
|
|
135
|
+
* - GET /callback - Handle OAuth callback, set session cookie
|
|
136
|
+
* - GET /logout - Clear cookie, redirect
|
|
137
|
+
* - POST /logout - Clear cookie, JSON response
|
|
138
|
+
* - GET /me - Return session user info
|
|
139
|
+
* - POST /refresh - Refresh access token
|
|
140
|
+
*/
|
|
141
|
+
declare function createOAuthRoutes(options?: OAuthRoutesOptions): Hono;
|
|
142
|
+
|
|
143
|
+
export { type OAuthRoutesOptions, type SessionAuthOptions, type SessionEnv, type SessionUser, clearSessionCookie, createOAuthRoutes, getSessionFromCookie, requireSession, sessionAuth, setSessionCookie };
|
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { setCookie, deleteCookie, getCookie } from 'hono/cookie';
|
|
3
|
+
|
|
4
|
+
// src/session-hono.ts
|
|
5
|
+
|
|
6
|
+
// src/session.ts
|
|
7
|
+
var defaultSessionConfig = {
|
|
8
|
+
cookieName: "session",
|
|
9
|
+
cookieMaxAge: 60 * 60 * 24 * 7,
|
|
10
|
+
// 7 days
|
|
11
|
+
cookieSecure: true,
|
|
12
|
+
cookieSameSite: "lax",
|
|
13
|
+
secret: "oauth-do-dev-secret-change-in-production"
|
|
14
|
+
};
|
|
15
|
+
var ALGORITHM = "AES-GCM";
|
|
16
|
+
var IV_LENGTH = 12;
|
|
17
|
+
var TAG_LENGTH = 128;
|
|
18
|
+
async function getEncryptionKey(secret) {
|
|
19
|
+
const encoder = new TextEncoder();
|
|
20
|
+
return crypto.subtle.importKey(
|
|
21
|
+
"raw",
|
|
22
|
+
encoder.encode(secret.padEnd(32, "0").slice(0, 32)),
|
|
23
|
+
{ name: ALGORITHM },
|
|
24
|
+
false,
|
|
25
|
+
["encrypt", "decrypt"]
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
async function encodeSession(session, secret) {
|
|
29
|
+
const key = await getEncryptionKey(secret ?? defaultSessionConfig.secret);
|
|
30
|
+
const iv = crypto.getRandomValues(new Uint8Array(IV_LENGTH));
|
|
31
|
+
const encoder = new TextEncoder();
|
|
32
|
+
const data = encoder.encode(JSON.stringify(session));
|
|
33
|
+
const ciphertext = await crypto.subtle.encrypt(
|
|
34
|
+
{ name: ALGORITHM, iv, tagLength: TAG_LENGTH },
|
|
35
|
+
key,
|
|
36
|
+
data
|
|
37
|
+
);
|
|
38
|
+
const combined = new Uint8Array(iv.length + ciphertext.byteLength);
|
|
39
|
+
combined.set(iv, 0);
|
|
40
|
+
combined.set(new Uint8Array(ciphertext), iv.length);
|
|
41
|
+
return btoa(String.fromCharCode(...combined));
|
|
42
|
+
}
|
|
43
|
+
async function decodeSession(encoded, secret) {
|
|
44
|
+
try {
|
|
45
|
+
const key = await getEncryptionKey(secret ?? defaultSessionConfig.secret);
|
|
46
|
+
const combined = Uint8Array.from(atob(encoded), (c) => c.charCodeAt(0));
|
|
47
|
+
const iv = combined.slice(0, IV_LENGTH);
|
|
48
|
+
const ciphertext = combined.slice(IV_LENGTH);
|
|
49
|
+
const decrypted = await crypto.subtle.decrypt(
|
|
50
|
+
{ name: ALGORITHM, iv, tagLength: TAG_LENGTH },
|
|
51
|
+
key,
|
|
52
|
+
ciphertext
|
|
53
|
+
);
|
|
54
|
+
const decoder = new TextDecoder();
|
|
55
|
+
const parsed = JSON.parse(decoder.decode(decrypted));
|
|
56
|
+
if (!isValidSessionData(parsed)) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
return parsed;
|
|
60
|
+
} catch {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function isValidSessionData(data) {
|
|
65
|
+
if (data === null || typeof data !== "object") {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
const session = data;
|
|
69
|
+
if (typeof session.userId !== "string" || session.userId.length === 0) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
if (typeof session.accessToken !== "string" || session.accessToken.length === 0) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
if (session.organizationId !== void 0 && typeof session.organizationId !== "string") {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
if (session.email !== void 0 && typeof session.email !== "string") {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
if (session.name !== void 0 && typeof session.name !== "string") {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
if (session.refreshToken !== void 0 && typeof session.refreshToken !== "string") {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
if (session.expiresAt !== void 0 && typeof session.expiresAt !== "number") {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
function getSessionConfig(env) {
|
|
93
|
+
const validSameSite = ["strict", "lax", "none"];
|
|
94
|
+
let cookieSameSite = defaultSessionConfig.cookieSameSite;
|
|
95
|
+
if (env?.SESSION_COOKIE_SAME_SITE) {
|
|
96
|
+
const value = env.SESSION_COOKIE_SAME_SITE;
|
|
97
|
+
if (validSameSite.includes(value)) {
|
|
98
|
+
cookieSameSite = value;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
let cookieMaxAge = defaultSessionConfig.cookieMaxAge;
|
|
102
|
+
if (env?.SESSION_COOKIE_MAX_AGE) {
|
|
103
|
+
const parsed = parseInt(env.SESSION_COOKIE_MAX_AGE, 10);
|
|
104
|
+
if (!Number.isNaN(parsed) && parsed > 0) {
|
|
105
|
+
cookieMaxAge = parsed;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
secret: env?.SESSION_SECRET ?? defaultSessionConfig.secret,
|
|
110
|
+
cookieName: env?.SESSION_COOKIE_NAME ?? defaultSessionConfig.cookieName,
|
|
111
|
+
cookieMaxAge,
|
|
112
|
+
cookieSecure: env?.SESSION_COOKIE_SECURE !== "false",
|
|
113
|
+
cookieSameSite
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// src/session-hono.ts
|
|
118
|
+
async function setSessionCookie(c, session, config = defaultSessionConfig) {
|
|
119
|
+
const encoded = await encodeSession(session, config.secret);
|
|
120
|
+
setCookie(c, config.cookieName, encoded, {
|
|
121
|
+
path: "/",
|
|
122
|
+
httpOnly: true,
|
|
123
|
+
secure: config.cookieSecure,
|
|
124
|
+
sameSite: config.cookieSameSite === "none" ? "None" : config.cookieSameSite === "strict" ? "Strict" : "Lax",
|
|
125
|
+
maxAge: config.cookieMaxAge
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
function clearSessionCookie(c, config = defaultSessionConfig) {
|
|
129
|
+
deleteCookie(c, config.cookieName, { path: "/" });
|
|
130
|
+
}
|
|
131
|
+
async function getSessionFromCookie(c, config = defaultSessionConfig) {
|
|
132
|
+
const encoded = getCookie(c, config.cookieName);
|
|
133
|
+
if (!encoded) return null;
|
|
134
|
+
return decodeSession(encoded, config.secret);
|
|
135
|
+
}
|
|
136
|
+
function sessionAuth(options = {}) {
|
|
137
|
+
return async (c, next) => {
|
|
138
|
+
const env = c.env ?? {};
|
|
139
|
+
const config = { ...getSessionConfig(env), ...options.config };
|
|
140
|
+
const session = await getSessionFromCookie(c, config);
|
|
141
|
+
c.set("session", session);
|
|
142
|
+
c.set("sessionUser", session ? {
|
|
143
|
+
id: session.userId,
|
|
144
|
+
email: session.email,
|
|
145
|
+
name: session.name,
|
|
146
|
+
organizationId: session.organizationId
|
|
147
|
+
} : null);
|
|
148
|
+
await next();
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
function requireSession(options = {}) {
|
|
152
|
+
return async (c, next) => {
|
|
153
|
+
const env = c.env ?? {};
|
|
154
|
+
const config = { ...getSessionConfig(env), ...options.config };
|
|
155
|
+
const session = await getSessionFromCookie(c, config);
|
|
156
|
+
if (!session) {
|
|
157
|
+
return c.json({ error: "Unauthorized", message: "Authentication required" }, 401);
|
|
158
|
+
}
|
|
159
|
+
if (session.expiresAt && Date.now() >= session.expiresAt) {
|
|
160
|
+
clearSessionCookie(c, config);
|
|
161
|
+
return c.json({ error: "Unauthorized", message: "Session expired" }, 401);
|
|
162
|
+
}
|
|
163
|
+
c.set("session", session);
|
|
164
|
+
c.set("sessionUser", {
|
|
165
|
+
id: session.userId,
|
|
166
|
+
email: session.email,
|
|
167
|
+
name: session.name,
|
|
168
|
+
organizationId: session.organizationId
|
|
169
|
+
});
|
|
170
|
+
await next();
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
function createWorkOSClient(apiKey) {
|
|
174
|
+
const baseUrl = "https://api.workos.com";
|
|
175
|
+
return {
|
|
176
|
+
getAuthorizationUrl(options) {
|
|
177
|
+
const params = new URLSearchParams({
|
|
178
|
+
client_id: options.clientId,
|
|
179
|
+
redirect_uri: options.redirectUri,
|
|
180
|
+
response_type: "code",
|
|
181
|
+
...options.state && { state: options.state },
|
|
182
|
+
...options.provider && { provider: options.provider }
|
|
183
|
+
});
|
|
184
|
+
return `https://api.workos.com/sso/authorize?${params.toString()}`;
|
|
185
|
+
},
|
|
186
|
+
async authenticateWithCode(options) {
|
|
187
|
+
const response = await fetch(`${baseUrl}/user_management/authenticate`, {
|
|
188
|
+
method: "POST",
|
|
189
|
+
headers: {
|
|
190
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
191
|
+
"Authorization": `Bearer ${apiKey}`
|
|
192
|
+
},
|
|
193
|
+
body: new URLSearchParams({
|
|
194
|
+
grant_type: "authorization_code",
|
|
195
|
+
client_id: options.clientId,
|
|
196
|
+
code: options.code,
|
|
197
|
+
redirect_uri: options.redirectUri
|
|
198
|
+
}).toString()
|
|
199
|
+
});
|
|
200
|
+
if (!response.ok) {
|
|
201
|
+
const error = await response.text();
|
|
202
|
+
throw new Error(`WorkOS authentication failed: ${response.status} - ${error}`);
|
|
203
|
+
}
|
|
204
|
+
return response.json();
|
|
205
|
+
},
|
|
206
|
+
async refreshToken(options) {
|
|
207
|
+
const response = await fetch(`${baseUrl}/user_management/authenticate`, {
|
|
208
|
+
method: "POST",
|
|
209
|
+
headers: {
|
|
210
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
211
|
+
"Authorization": `Bearer ${apiKey}`
|
|
212
|
+
},
|
|
213
|
+
body: new URLSearchParams({
|
|
214
|
+
grant_type: "refresh_token",
|
|
215
|
+
client_id: options.clientId,
|
|
216
|
+
refresh_token: options.refreshToken
|
|
217
|
+
}).toString()
|
|
218
|
+
});
|
|
219
|
+
if (!response.ok) {
|
|
220
|
+
const error = await response.text();
|
|
221
|
+
throw new Error(`Token refresh failed: ${response.status} - ${error}`);
|
|
222
|
+
}
|
|
223
|
+
return response.json();
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
function createOAuthRoutes(options = {}) {
|
|
228
|
+
const app = new Hono();
|
|
229
|
+
app.use("*", async (c, next) => {
|
|
230
|
+
await next();
|
|
231
|
+
const contentType = c.res.headers.get("content-type");
|
|
232
|
+
if (contentType?.includes("application/json") && !contentType.includes("charset")) {
|
|
233
|
+
c.res.headers.set("content-type", "application/json; charset=utf-8");
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
app.get("/login", (c) => {
|
|
237
|
+
const env = c.env ?? {};
|
|
238
|
+
const apiKey = options.workosApiKey ?? env.WORKOS_API_KEY;
|
|
239
|
+
const clientId = options.clientId ?? env.WORKOS_CLIENT_ID;
|
|
240
|
+
if (!apiKey) {
|
|
241
|
+
return c.json({ error: "Server configuration error", message: "WORKOS_API_KEY not configured" }, 500);
|
|
242
|
+
}
|
|
243
|
+
if (!clientId) {
|
|
244
|
+
return c.json({ error: "Server configuration error", message: "WORKOS_CLIENT_ID not configured" }, 500);
|
|
245
|
+
}
|
|
246
|
+
const workos = createWorkOSClient(apiKey);
|
|
247
|
+
const url = new URL(c.req.url);
|
|
248
|
+
const baseUrl = options.redirectBaseUrl ?? env.AUTH_REDIRECT_BASE_URL ?? `${url.protocol}//${url.host}`;
|
|
249
|
+
const intendedRedirect = c.req.query("redirect_uri") || "/";
|
|
250
|
+
const provider = c.req.query("provider");
|
|
251
|
+
const state = btoa(JSON.stringify({ redirect: intendedRedirect }));
|
|
252
|
+
const authUrl = workos.getAuthorizationUrl({
|
|
253
|
+
clientId,
|
|
254
|
+
redirectUri: `${baseUrl}/auth/callback`,
|
|
255
|
+
state,
|
|
256
|
+
provider
|
|
257
|
+
});
|
|
258
|
+
return c.redirect(authUrl);
|
|
259
|
+
});
|
|
260
|
+
app.get("/callback", async (c) => {
|
|
261
|
+
const env = c.env ?? {};
|
|
262
|
+
const apiKey = options.workosApiKey ?? env.WORKOS_API_KEY;
|
|
263
|
+
const clientId = options.clientId ?? env.WORKOS_CLIENT_ID;
|
|
264
|
+
const sessionConfig = { ...getSessionConfig(env), ...options.session };
|
|
265
|
+
const debug = options.debug ?? env.DEBUG === "true";
|
|
266
|
+
if (!apiKey) {
|
|
267
|
+
return c.json({ error: "Server configuration error", message: "WORKOS_API_KEY not configured" }, 500);
|
|
268
|
+
}
|
|
269
|
+
if (!clientId) {
|
|
270
|
+
return c.json({ error: "Server configuration error", message: "WORKOS_CLIENT_ID not configured" }, 500);
|
|
271
|
+
}
|
|
272
|
+
const code = c.req.query("code");
|
|
273
|
+
const state = c.req.query("state");
|
|
274
|
+
const error = c.req.query("error");
|
|
275
|
+
const errorDescription = c.req.query("error_description");
|
|
276
|
+
if (error) {
|
|
277
|
+
if (debug) console.error("[Auth] OAuth error:", error, errorDescription);
|
|
278
|
+
return c.json({ error: "Authentication failed", message: errorDescription || error }, 400);
|
|
279
|
+
}
|
|
280
|
+
if (!code) {
|
|
281
|
+
return c.json({ error: "Missing authorization code" }, 400);
|
|
282
|
+
}
|
|
283
|
+
const workos = createWorkOSClient(apiKey);
|
|
284
|
+
const url = new URL(c.req.url);
|
|
285
|
+
const baseUrl = options.redirectBaseUrl ?? env.AUTH_REDIRECT_BASE_URL ?? `${url.protocol}//${url.host}`;
|
|
286
|
+
try {
|
|
287
|
+
const result = await workos.authenticateWithCode({
|
|
288
|
+
clientId,
|
|
289
|
+
code,
|
|
290
|
+
redirectUri: `${baseUrl}/auth/callback`
|
|
291
|
+
});
|
|
292
|
+
const session = {
|
|
293
|
+
userId: result.user.id,
|
|
294
|
+
organizationId: result.user.organization_id,
|
|
295
|
+
email: result.user.email,
|
|
296
|
+
name: [result.user.first_name, result.user.last_name].filter(Boolean).join(" ") || void 0,
|
|
297
|
+
accessToken: result.access_token,
|
|
298
|
+
refreshToken: result.refresh_token,
|
|
299
|
+
expiresAt: result.expires_in ? Date.now() + result.expires_in * 1e3 : void 0
|
|
300
|
+
};
|
|
301
|
+
await setSessionCookie(c, session, sessionConfig);
|
|
302
|
+
await options.onLogin?.(session, c);
|
|
303
|
+
let redirectTo = "/";
|
|
304
|
+
if (state) {
|
|
305
|
+
try {
|
|
306
|
+
const stateData = JSON.parse(atob(state));
|
|
307
|
+
redirectTo = stateData.redirect || "/";
|
|
308
|
+
} catch {
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
if (!redirectTo.startsWith("/")) {
|
|
312
|
+
redirectTo = "/";
|
|
313
|
+
}
|
|
314
|
+
return c.redirect(redirectTo);
|
|
315
|
+
} catch (err) {
|
|
316
|
+
if (debug) console.error("[Auth] OAuth callback error:", err);
|
|
317
|
+
return c.json({
|
|
318
|
+
error: "Authentication failed",
|
|
319
|
+
message: err instanceof Error ? err.message : "Unknown error"
|
|
320
|
+
}, 500);
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
app.get("/logout", async (c) => {
|
|
324
|
+
const env = c.env ?? {};
|
|
325
|
+
const sessionConfig = { ...getSessionConfig(env), ...options.session };
|
|
326
|
+
clearSessionCookie(c, sessionConfig);
|
|
327
|
+
await options.onLogout?.(c);
|
|
328
|
+
const redirectTo = c.req.query("redirect_uri") || "/";
|
|
329
|
+
if (!redirectTo.startsWith("/")) {
|
|
330
|
+
return c.redirect("/");
|
|
331
|
+
}
|
|
332
|
+
return c.redirect(redirectTo);
|
|
333
|
+
});
|
|
334
|
+
app.post("/logout", async (c) => {
|
|
335
|
+
const env = c.env ?? {};
|
|
336
|
+
const sessionConfig = { ...getSessionConfig(env), ...options.session };
|
|
337
|
+
clearSessionCookie(c, sessionConfig);
|
|
338
|
+
await options.onLogout?.(c);
|
|
339
|
+
return c.json({ success: true, message: "Logged out successfully" });
|
|
340
|
+
});
|
|
341
|
+
app.get("/me", async (c) => {
|
|
342
|
+
const env = c.env ?? {};
|
|
343
|
+
const sessionConfig = { ...getSessionConfig(env), ...options.session };
|
|
344
|
+
const session = await getSessionFromCookie(c, sessionConfig);
|
|
345
|
+
if (!session) {
|
|
346
|
+
return c.json({ error: "Unauthorized", message: "Not authenticated" }, 401);
|
|
347
|
+
}
|
|
348
|
+
if (session.expiresAt && Date.now() >= session.expiresAt) {
|
|
349
|
+
clearSessionCookie(c, sessionConfig);
|
|
350
|
+
return c.json({ error: "Unauthorized", message: "Session expired" }, 401);
|
|
351
|
+
}
|
|
352
|
+
return c.json({
|
|
353
|
+
id: session.userId,
|
|
354
|
+
email: session.email,
|
|
355
|
+
name: session.name,
|
|
356
|
+
organizationId: session.organizationId
|
|
357
|
+
});
|
|
358
|
+
});
|
|
359
|
+
app.post("/refresh", async (c) => {
|
|
360
|
+
const env = c.env ?? {};
|
|
361
|
+
const apiKey = options.workosApiKey ?? env.WORKOS_API_KEY;
|
|
362
|
+
const clientId = options.clientId ?? env.WORKOS_CLIENT_ID;
|
|
363
|
+
const sessionConfig = { ...getSessionConfig(env), ...options.session };
|
|
364
|
+
const debug = options.debug ?? env.DEBUG === "true";
|
|
365
|
+
if (!apiKey) {
|
|
366
|
+
return c.json({ error: "Server configuration error", message: "WORKOS_API_KEY not configured" }, 500);
|
|
367
|
+
}
|
|
368
|
+
if (!clientId) {
|
|
369
|
+
return c.json({ error: "Server configuration error", message: "WORKOS_CLIENT_ID not configured" }, 500);
|
|
370
|
+
}
|
|
371
|
+
const session = await getSessionFromCookie(c, sessionConfig);
|
|
372
|
+
if (!session) {
|
|
373
|
+
return c.json({ error: "Unauthorized", message: "Not authenticated" }, 401);
|
|
374
|
+
}
|
|
375
|
+
if (!session.refreshToken) {
|
|
376
|
+
return c.json({ error: "Cannot refresh", message: "No refresh token available" }, 400);
|
|
377
|
+
}
|
|
378
|
+
const workos = createWorkOSClient(apiKey);
|
|
379
|
+
try {
|
|
380
|
+
const result = await workos.refreshToken({
|
|
381
|
+
clientId,
|
|
382
|
+
refreshToken: session.refreshToken
|
|
383
|
+
});
|
|
384
|
+
const updatedSession = {
|
|
385
|
+
...session,
|
|
386
|
+
accessToken: result.access_token,
|
|
387
|
+
refreshToken: result.refresh_token || session.refreshToken,
|
|
388
|
+
expiresAt: result.expires_in ? Date.now() + result.expires_in * 1e3 : void 0
|
|
389
|
+
};
|
|
390
|
+
await setSessionCookie(c, updatedSession, sessionConfig);
|
|
391
|
+
return c.json({ success: true, expiresAt: updatedSession.expiresAt });
|
|
392
|
+
} catch (err) {
|
|
393
|
+
if (debug) console.error("[Auth] Token refresh error:", err);
|
|
394
|
+
clearSessionCookie(c, sessionConfig);
|
|
395
|
+
return c.json({
|
|
396
|
+
error: "Refresh failed",
|
|
397
|
+
message: err instanceof Error ? err.message : "Unknown error"
|
|
398
|
+
}, 401);
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
return app;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
export { clearSessionCookie, createOAuthRoutes, getSessionFromCookie, requireSession, sessionAuth, setSessionCookie };
|
|
405
|
+
//# sourceMappingURL=session-hono.js.map
|
|
406
|
+
//# sourceMappingURL=session-hono.js.map
|