@hono/auth-js 1.0.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/README.md ADDED
@@ -0,0 +1,143 @@
1
+ # Auth.js middleware for Hono
2
+
3
+ This is a [Auth.js](https://authjs.dev) third-party middleware for [Hono](https://github.com/honojs/hono).
4
+
5
+ This middleware can be used to inject the Auth.js session into the request context.
6
+
7
+ ## Installation
8
+
9
+ ```plain
10
+ npm i hono @hono/auth-js @auth/core
11
+ ```
12
+
13
+ ## Configuration
14
+
15
+ Before starting using the middleware you must set the following environment variables:
16
+
17
+ ```plain
18
+ AUTH_SECRET=#required
19
+ AUTH_URL=#optional
20
+ ```
21
+
22
+ ## How to Use
23
+
24
+ ```ts
25
+ import { Hono,Context } from 'hono'
26
+ import { authHandler, initAuthConfig, verifyAuth, AuthConfig } from "@hono/auth-js"
27
+
28
+ const app = new Hono()
29
+
30
+ app.use("*", initAuthConfig(getAuthConfig))
31
+
32
+ app.use("/api/auth/*", authHandler())
33
+
34
+ app.use('/api/*', verifyAuth())
35
+
36
+ app.get('/api/protected', (c) => {
37
+ const auth = c.get("authUser")
38
+ return c.json(auth)
39
+ })
40
+
41
+ function getAuthConfig(c: Context): AuthConfig {
42
+ return {
43
+ secret: c.env.AUTH_SECRET,
44
+ providers: [
45
+ GitHub({
46
+ clientId: c.env.GITHUB_ID,
47
+ clientSecret: c.env.GITHUB_SECRET
48
+ }),
49
+ ]
50
+ }
51
+ }
52
+
53
+ export default app
54
+ ```
55
+
56
+ React component
57
+ ```tsx
58
+ import { SessionProvider } from "@hono/auth-js/react"
59
+
60
+ export default function App() {
61
+
62
+ return (
63
+ <SessionProvider>
64
+ <Children />
65
+ </SessionProvider>
66
+ )
67
+ }
68
+
69
+ function Children() {
70
+ const { data: session, status } = useSession()
71
+ return (
72
+ <div >
73
+ I am {session?.user}
74
+ </div>
75
+ )
76
+ }
77
+ ```
78
+ Default `/api/auth` path can be changed to something else but that will also require you to change path in react app.
79
+
80
+ ```tsx
81
+ import {SessionProvider,authConfigManager,useSession } from "@hono/auth-js/react"
82
+
83
+ authConfigManager.setConfig({
84
+ baseUrl: '', //needed for cross domain setup.
85
+ basePath: '/custom', // if auth route is diff from /api/auth
86
+ credentials:'same-origin' //needed for cross domain setup
87
+ });
88
+
89
+ export default function App() {
90
+ return (
91
+ <SessionProvider>
92
+ <Children />
93
+ </SessionProvider>
94
+ )
95
+ }
96
+
97
+ function Children() {
98
+ const { data: session, status } = useSession()
99
+ return (
100
+ <div >
101
+ I am {session?.user}
102
+ </div>
103
+ )
104
+ }
105
+ ```
106
+ For cross domain setup as mentioned above you need to set these cors headers in hono along with change in same site cookie attribute.[Read More Here](https://next-auth.js.org/configuration/options#cookies)
107
+ ``` ts
108
+ app.use(
109
+ "*",
110
+ cors({
111
+ origin: (origin) => origin,
112
+ allowHeaders: ["Content-Type"],
113
+ credentials: true,
114
+ })
115
+ )
116
+ ```
117
+
118
+
119
+ SessionProvider is not needed with react query.This wrapper is enough
120
+
121
+ ```ts
122
+ const useSession = ()=>{
123
+ const { data ,status } = useQuery({
124
+ queryKey: ["session"],
125
+ queryFn: async () => {
126
+ const res = await fetch("/api/auth/session")
127
+ return res.json();
128
+ },
129
+ staleTime: 5 * (60 * 1000),
130
+ gcTime: 10 * (60 * 1000),
131
+ refetchOnWindowFocus: true,
132
+ })
133
+ return { session:data, status }
134
+ }
135
+ ```
136
+ > [!WARNING]
137
+ > You can't use event updates which SessionProvider provides and session will not be in sync across tabs if you use react query wrapper but in RQ5 you can enable this using Broadcast channel (see RQ docs).
138
+
139
+ Working example repo https://github.com/divyam234/next-auth-hono-react
140
+
141
+ ## Author
142
+
143
+ Divyam <https://github.com/divyam234>
@@ -0,0 +1,31 @@
1
+ import { AuthConfig as AuthConfig$1 } from '@auth/core';
2
+ import { AdapterUser } from '@auth/core/adapters';
3
+ import { JWT } from '@auth/core/jwt';
4
+ import { Session } from '@auth/core/types';
5
+ import { Context, MiddlewareHandler } from 'hono';
6
+
7
+ declare module 'hono' {
8
+ interface ContextVariableMap {
9
+ authUser: AuthUser;
10
+ authConfig: AuthConfig;
11
+ }
12
+ }
13
+ type AuthEnv = {
14
+ AUTH_SECRET: string;
15
+ AUTH_REDIRECT_PROXY_URL?: string;
16
+ [key: string]: string | undefined;
17
+ };
18
+ type AuthUser = {
19
+ session: Session;
20
+ token?: JWT;
21
+ user?: AdapterUser;
22
+ };
23
+ interface AuthConfig extends Omit<AuthConfig$1, 'raw'> {
24
+ }
25
+ type ConfigHandler = (c: Context) => AuthConfig;
26
+ declare function getAuthUser(c: Context): Promise<AuthUser | null>;
27
+ declare function verifyAuth(): MiddlewareHandler;
28
+ declare function initAuthConfig(cb: ConfigHandler): MiddlewareHandler;
29
+ declare function authHandler(): MiddlewareHandler;
30
+
31
+ export { type AuthConfig, type AuthEnv, type AuthUser, type ConfigHandler, authHandler, getAuthUser, initAuthConfig, verifyAuth };
@@ -0,0 +1,31 @@
1
+ import { AuthConfig as AuthConfig$1 } from '@auth/core';
2
+ import { AdapterUser } from '@auth/core/adapters';
3
+ import { JWT } from '@auth/core/jwt';
4
+ import { Session } from '@auth/core/types';
5
+ import { Context, MiddlewareHandler } from 'hono';
6
+
7
+ declare module 'hono' {
8
+ interface ContextVariableMap {
9
+ authUser: AuthUser;
10
+ authConfig: AuthConfig;
11
+ }
12
+ }
13
+ type AuthEnv = {
14
+ AUTH_SECRET: string;
15
+ AUTH_REDIRECT_PROXY_URL?: string;
16
+ [key: string]: string | undefined;
17
+ };
18
+ type AuthUser = {
19
+ session: Session;
20
+ token?: JWT;
21
+ user?: AdapterUser;
22
+ };
23
+ interface AuthConfig extends Omit<AuthConfig$1, 'raw'> {
24
+ }
25
+ type ConfigHandler = (c: Context) => AuthConfig;
26
+ declare function getAuthUser(c: Context): Promise<AuthUser | null>;
27
+ declare function verifyAuth(): MiddlewareHandler;
28
+ declare function initAuthConfig(cb: ConfigHandler): MiddlewareHandler;
29
+ declare function authHandler(): MiddlewareHandler;
30
+
31
+ export { type AuthConfig, type AuthEnv, type AuthUser, type ConfigHandler, authHandler, getAuthUser, initAuthConfig, verifyAuth };
package/dist/index.js ADDED
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ authHandler: () => authHandler,
24
+ getAuthUser: () => getAuthUser,
25
+ initAuthConfig: () => initAuthConfig,
26
+ verifyAuth: () => verifyAuth
27
+ });
28
+ module.exports = __toCommonJS(src_exports);
29
+ var import_core = require("@auth/core");
30
+ var import_http_exception = require("hono/http-exception");
31
+ function reqWithEnvUrl(req, authUrl) {
32
+ return authUrl ? new Request(new URL(req.url, authUrl).href, req) : req;
33
+ }
34
+ function setEnvDefaults(env, config) {
35
+ config.secret ??= env.AUTH_SECRET;
36
+ config.trustHost = true;
37
+ config.redirectProxyUrl ??= env.AUTH_REDIRECT_PROXY_URL;
38
+ config.providers = config.providers.map((p) => {
39
+ const finalProvider = typeof p === "function" ? p({}) : p;
40
+ if (finalProvider.type === "oauth" || finalProvider.type === "oidc") {
41
+ const ID = finalProvider.id.toUpperCase();
42
+ finalProvider.clientId ??= env[`AUTH_${ID}_ID`];
43
+ finalProvider.clientSecret ??= env[`AUTH_${ID}_SECRET`];
44
+ if (finalProvider.type === "oidc") {
45
+ finalProvider.issuer ??= env[`AUTH_${ID}_ISSUER`];
46
+ }
47
+ }
48
+ return finalProvider;
49
+ });
50
+ }
51
+ async function getAuthUser(c) {
52
+ const config = c.get("authConfig");
53
+ const origin = new URL(c.req.url, c.env.AUTH_URL).origin;
54
+ const request = new Request(`${origin}/session`, {
55
+ headers: { cookie: c.req.header("cookie") ?? "" }
56
+ });
57
+ setEnvDefaults(c.env, config);
58
+ let authUser = {};
59
+ const response = await (0, import_core.Auth)(request, {
60
+ ...config,
61
+ callbacks: {
62
+ ...config.callbacks,
63
+ async session(...args) {
64
+ authUser = args[0];
65
+ const session2 = await config.callbacks?.session?.(...args) ?? args[0].session;
66
+ const user = args[0].user ?? args[0].token;
67
+ return { user, ...session2 };
68
+ }
69
+ }
70
+ });
71
+ const session = await response.json();
72
+ return session && session.user ? authUser : null;
73
+ }
74
+ function verifyAuth() {
75
+ return async (c, next) => {
76
+ const authUser = await getAuthUser(c);
77
+ const isAuth = !!authUser?.token || !!authUser?.user;
78
+ if (!isAuth) {
79
+ const res = new Response("Unauthorized", {
80
+ status: 401
81
+ });
82
+ throw new import_http_exception.HTTPException(401, { res });
83
+ } else
84
+ c.set("authUser", authUser);
85
+ await next();
86
+ };
87
+ }
88
+ function initAuthConfig(cb) {
89
+ return async (c, next) => {
90
+ const config = cb(c);
91
+ c.set("authConfig", config);
92
+ await next();
93
+ };
94
+ }
95
+ function authHandler() {
96
+ return async (c) => {
97
+ const config = c.get("authConfig");
98
+ setEnvDefaults(c.env, config);
99
+ if (!config.secret) {
100
+ throw new import_http_exception.HTTPException(500, { message: "Missing AUTH_SECRET" });
101
+ }
102
+ const res = await (0, import_core.Auth)(reqWithEnvUrl(c.req.raw, c.env.AUTH_URL), config);
103
+ return new Response(res.body, res);
104
+ };
105
+ }
106
+ // Annotate the CommonJS export names for ESM import in node:
107
+ 0 && (module.exports = {
108
+ authHandler,
109
+ getAuthUser,
110
+ initAuthConfig,
111
+ verifyAuth
112
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,84 @@
1
+ // src/index.ts
2
+ import { Auth } from "@auth/core";
3
+ import { HTTPException } from "hono/http-exception";
4
+ function reqWithEnvUrl(req, authUrl) {
5
+ return authUrl ? new Request(new URL(req.url, authUrl).href, req) : req;
6
+ }
7
+ function setEnvDefaults(env, config) {
8
+ config.secret ??= env.AUTH_SECRET;
9
+ config.trustHost = true;
10
+ config.redirectProxyUrl ??= env.AUTH_REDIRECT_PROXY_URL;
11
+ config.providers = config.providers.map((p) => {
12
+ const finalProvider = typeof p === "function" ? p({}) : p;
13
+ if (finalProvider.type === "oauth" || finalProvider.type === "oidc") {
14
+ const ID = finalProvider.id.toUpperCase();
15
+ finalProvider.clientId ??= env[`AUTH_${ID}_ID`];
16
+ finalProvider.clientSecret ??= env[`AUTH_${ID}_SECRET`];
17
+ if (finalProvider.type === "oidc") {
18
+ finalProvider.issuer ??= env[`AUTH_${ID}_ISSUER`];
19
+ }
20
+ }
21
+ return finalProvider;
22
+ });
23
+ }
24
+ async function getAuthUser(c) {
25
+ const config = c.get("authConfig");
26
+ const origin = new URL(c.req.url, c.env.AUTH_URL).origin;
27
+ const request = new Request(`${origin}/session`, {
28
+ headers: { cookie: c.req.header("cookie") ?? "" }
29
+ });
30
+ setEnvDefaults(c.env, config);
31
+ let authUser = {};
32
+ const response = await Auth(request, {
33
+ ...config,
34
+ callbacks: {
35
+ ...config.callbacks,
36
+ async session(...args) {
37
+ authUser = args[0];
38
+ const session2 = await config.callbacks?.session?.(...args) ?? args[0].session;
39
+ const user = args[0].user ?? args[0].token;
40
+ return { user, ...session2 };
41
+ }
42
+ }
43
+ });
44
+ const session = await response.json();
45
+ return session && session.user ? authUser : null;
46
+ }
47
+ function verifyAuth() {
48
+ return async (c, next) => {
49
+ const authUser = await getAuthUser(c);
50
+ const isAuth = !!authUser?.token || !!authUser?.user;
51
+ if (!isAuth) {
52
+ const res = new Response("Unauthorized", {
53
+ status: 401
54
+ });
55
+ throw new HTTPException(401, { res });
56
+ } else
57
+ c.set("authUser", authUser);
58
+ await next();
59
+ };
60
+ }
61
+ function initAuthConfig(cb) {
62
+ return async (c, next) => {
63
+ const config = cb(c);
64
+ c.set("authConfig", config);
65
+ await next();
66
+ };
67
+ }
68
+ function authHandler() {
69
+ return async (c) => {
70
+ const config = c.get("authConfig");
71
+ setEnvDefaults(c.env, config);
72
+ if (!config.secret) {
73
+ throw new HTTPException(500, { message: "Missing AUTH_SECRET" });
74
+ }
75
+ const res = await Auth(reqWithEnvUrl(c.req.raw, c.env.AUTH_URL), config);
76
+ return new Response(res.body, res);
77
+ };
78
+ }
79
+ export {
80
+ authHandler,
81
+ getAuthUser,
82
+ initAuthConfig,
83
+ verifyAuth
84
+ };
@@ -0,0 +1,153 @@
1
+ import { BuiltInProviderType, ProviderType, RedirectableProviderType } from '@auth/core/providers';
2
+ import { Session } from '@auth/core/types';
3
+ import * as React from 'react';
4
+
5
+ interface AuthClientConfig {
6
+ baseUrl: string;
7
+ basePath: string;
8
+ credentials?: RequestCredentials;
9
+ /** Stores last session response */
10
+ _session?: Session | null | undefined;
11
+ /** Used for timestamp since last sycned (in seconds) */
12
+ _lastSync: number;
13
+ /**
14
+ * Stores the `SessionProvider`'s session update method to be able to
15
+ * trigger session updates from places like `signIn` or `signOut`
16
+ */
17
+ _getSession: (...args: any[]) => any;
18
+ }
19
+ interface UseSessionOptions<R extends boolean> {
20
+ required: R;
21
+ /** Defaults to `signIn` */
22
+ onUnauthenticated?: () => void;
23
+ }
24
+ type LiteralUnion<T extends U, U = string> = T | (U & Record<never, never>);
25
+ interface ClientSafeProvider {
26
+ id: LiteralUnion<BuiltInProviderType>;
27
+ name: string;
28
+ type: ProviderType;
29
+ signinUrl: string;
30
+ callbackUrl: string;
31
+ }
32
+ interface SignInOptions extends Record<string, unknown> {
33
+ /**
34
+ * Specify to which URL the user will be redirected after signing in. Defaults to the page URL the sign-in is initiated from.
35
+ *
36
+ * [Documentation](https://next-auth.js.org/getting-started/client#specifying-a-callbackurl)
37
+ */
38
+ callbackUrl?: string;
39
+ /** [Documentation](https://next-auth.js.org/getting-started/client#using-the-redirect-false-option) */
40
+ redirect?: boolean;
41
+ }
42
+ interface SignInResponse {
43
+ error: string | undefined;
44
+ status: number;
45
+ ok: boolean;
46
+ url: string | null;
47
+ }
48
+ /**
49
+ * Match `inputType` of `new URLSearchParams(inputType)`
50
+ * @internal
51
+ */
52
+ type SignInAuthorizationParams = string | string[][] | Record<string, string> | URLSearchParams;
53
+ /** [Documentation](https://next-auth.js.org/getting-started/client#using-the-redirect-false-option-1) */
54
+ interface SignOutResponse {
55
+ url: string;
56
+ }
57
+ interface SignOutParams<R extends boolean = true> {
58
+ /** [Documentation](https://next-auth.js.org/getting-started/client#specifying-a-callbackurl-1) */
59
+ callbackUrl?: string;
60
+ /** [Documentation](https://next-auth.js.org/getting-started/client#using-the-redirect-false-option-1 */
61
+ redirect?: R;
62
+ }
63
+ /**
64
+
65
+ * If you have session expiry times of 30 days (the default) or more, then you probably don't need to change any of the default options.
66
+ *
67
+ * However, if you need to customize the session behavior and/or are using short session expiry times, you can pass options to the provider to customize the behavior of the {@link useSession} hook.
68
+ */
69
+ interface SessionProviderProps {
70
+ children: React.ReactNode;
71
+ session?: Session | null;
72
+ baseUrl?: string;
73
+ basePath?: string;
74
+ /**
75
+ * A time interval (in seconds) after which the session will be re-fetched.
76
+ * If set to `0` (default), the session is not polled.
77
+ */
78
+ refetchInterval?: number;
79
+ /**
80
+ * `SessionProvider` automatically refetches the session when the user switches between windows.
81
+ * This option activates this behaviour if set to `true` (default).
82
+ */
83
+ refetchOnWindowFocus?: boolean;
84
+ /**
85
+ * Set to `false` to stop polling when the device has no internet access offline (determined by `navigator.onLine`)
86
+ *
87
+ * [`navigator.onLine` documentation](https://developer.mozilla.org/en-US/docs/Web/API/NavigatorOnLine/onLine)
88
+ */
89
+ refetchWhenOffline?: false;
90
+ }
91
+
92
+ declare class AuthConfigManager {
93
+ private static instance;
94
+ _config: AuthClientConfig;
95
+ static getInstance(): AuthConfigManager;
96
+ setConfig(userConfig: Partial<AuthClientConfig>): void;
97
+ getConfig(): AuthClientConfig;
98
+ }
99
+ declare const authConfigManager: AuthConfigManager;
100
+ /** @todo Document */
101
+ type UpdateSession = (data?: any) => Promise<Session | null>;
102
+ type SessionContextValue<R extends boolean = false> = R extends true ? {
103
+ update: UpdateSession;
104
+ data: Session;
105
+ status: 'authenticated';
106
+ } | {
107
+ update: UpdateSession;
108
+ data: null;
109
+ status: 'loading';
110
+ } : {
111
+ update: UpdateSession;
112
+ data: Session;
113
+ status: 'authenticated';
114
+ } | {
115
+ update: UpdateSession;
116
+ data: null;
117
+ status: 'unauthenticated' | 'loading';
118
+ };
119
+ declare const SessionContext: React.Context<{
120
+ update: UpdateSession;
121
+ data: Session;
122
+ status: 'authenticated';
123
+ } | {
124
+ update: UpdateSession;
125
+ data: null;
126
+ status: 'unauthenticated' | 'loading';
127
+ } | undefined>;
128
+ declare function useSession<R extends boolean>(options?: UseSessionOptions<R>): SessionContextValue<R>;
129
+ interface GetSessionParams {
130
+ event?: 'storage' | 'timer' | 'hidden' | string;
131
+ triggerEvent?: boolean;
132
+ broadcast?: boolean;
133
+ }
134
+ declare function getSession(params?: GetSessionParams): Promise<Session | null>;
135
+ /**
136
+ * Returns the current Cross-Site Request Forgery Token (CSRF Token)
137
+ * required to make requests that changes state. (e.g. signing in or out, or updating the session).
138
+ *
139
+ * [CSRF Prevention: Double Submit Cookie](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#double-submit-cookie)
140
+ * @internal
141
+ */
142
+ declare function getCsrfToken(): Promise<string>;
143
+ type ProvidersType = Record<LiteralUnion<BuiltInProviderType>, ClientSafeProvider>;
144
+ declare function getProviders(): Promise<ProvidersType | null>;
145
+ declare function signIn<P extends RedirectableProviderType | undefined = undefined>(provider?: LiteralUnion<P extends RedirectableProviderType ? P | BuiltInProviderType : BuiltInProviderType>, options?: SignInOptions, authorizationParams?: SignInAuthorizationParams): Promise<P extends RedirectableProviderType ? SignInResponse | undefined : undefined>;
146
+ /**
147
+ * Initiate a signout, by destroying the current session.
148
+ * Handles CSRF protection.
149
+ */
150
+ declare function signOut<R extends boolean = true>(options?: SignOutParams<R>): Promise<R extends true ? undefined : SignOutResponse>;
151
+ declare function SessionProvider(props: SessionProviderProps): React.JSX.Element;
152
+
153
+ export { type GetSessionParams, type LiteralUnion, SessionContext, type SessionContextValue, SessionProvider, type SessionProviderProps, type SignInAuthorizationParams, type SignInOptions, type SignInResponse, type SignOutParams, type UpdateSession, authConfigManager, getCsrfToken, getProviders, getSession, signIn, signOut, useSession };