authhero 0.0.1 → 0.2.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/CHANGELOG.md ADDED
@@ -0,0 +1,13 @@
1
+ # authhero
2
+
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Update the package to support both esm and cjs
8
+
9
+ ## 0.1.0
10
+
11
+ ### Minor Changes
12
+
13
+ - a1212dc: Added a jwks route with mock data
package/README.md CHANGED
@@ -5,5 +5,5 @@ An open-source auth library for building a drop-in replacement for Auth0.
5
5
  Setup a new project with Authhero in 5 minutes or less:
6
6
 
7
7
  ```bash
8
- npx create-authhero
8
+ npx create authhero
9
9
  ```
package/package.json CHANGED
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "name": "authhero",
3
- "version": "0.0.1",
4
- "type": "module",
3
+ "version": "0.2.0",
5
4
  "main": "dist/index.js",
6
5
  "types": "dist/index.d.ts",
7
6
  "bin": {
@@ -18,6 +17,7 @@
18
17
  "hono": "^4.4.10"
19
18
  },
20
19
  "scripts": {
20
+ "dev": "bun --watch src/bun.ts",
21
21
  "build": "vite build",
22
22
  "start": "pnpm build && node dist/index.js"
23
23
  }
package/src/bun.ts ADDED
@@ -0,0 +1,16 @@
1
+ import { OpenAPIHono } from "@hono/zod-openapi";
2
+ import { Bindings } from "./types/Bindings";
3
+ import { Context } from "hono";
4
+ import { init } from "./";
5
+
6
+ const app = new OpenAPIHono<{ Bindings: Bindings }>();
7
+
8
+ const authhero = init();
9
+
10
+ app.get("/test", (ctx: Context) => {
11
+ return ctx.text("Hello, world!");
12
+ });
13
+
14
+ app.route("/", authhero);
15
+
16
+ export default app;
package/src/index.ts CHANGED
@@ -1,13 +1,34 @@
1
1
  import { OpenAPIHono } from "@hono/zod-openapi";
2
2
  import { Context } from "hono";
3
- import { Bindings } from "./types/Bindings";
3
+ import { Bindings, Variables } from "./types";
4
+ import { wellKnownRoutes } from "./routes/oauth2";
4
5
 
5
6
  export interface AuthHeroConfig {}
6
7
 
7
8
  export function init() {
8
- const app = new OpenAPIHono<{ Bindings: Bindings }>();
9
+ const rootApp = new OpenAPIHono<{ Bindings: Bindings }>();
9
10
 
10
- app.get("/test", (ctx: Context) => {
11
- return ctx.text("Hello, world!");
11
+ rootApp.get("/", (ctx: Context) => {
12
+ return ctx.text("Hello, authhero!");
12
13
  });
14
+
15
+ /**
16
+ * The oauth routes
17
+ */
18
+ const oauthApp = new OpenAPIHono<{
19
+ Bindings: Bindings;
20
+ Variables: Variables;
21
+ }>().route("/.well-known", wellKnownRoutes);
22
+
23
+ oauthApp.doc("/spec", {
24
+ openapi: "3.0.0",
25
+ info: {
26
+ version: "1.0.0",
27
+ title: "Oauth endpoints",
28
+ },
29
+ });
30
+
31
+ rootApp.route("/", oauthApp);
32
+
33
+ return rootApp;
13
34
  }
@@ -0,0 +1 @@
1
+ export * from "./well-known";
@@ -0,0 +1,179 @@
1
+ import {
2
+ Bindings,
3
+ jwksKeySchema,
4
+ // jwksSchema,
5
+ openIDConfigurationSchema,
6
+ } from "../../types";
7
+ import { OpenAPIHono, createRoute } from "@hono/zod-openapi";
8
+
9
+ export const wellKnownRoutes = new OpenAPIHono<{ Bindings: Bindings }>()
10
+ // --------------------------------
11
+ // GET /.well-known/jwks.json
12
+ // --------------------------------
13
+ .openapi(
14
+ createRoute({
15
+ tags: ["jwks"],
16
+ method: "get",
17
+ path: "/jwks.json",
18
+ request: {},
19
+ responses: {
20
+ 200: {
21
+ content: {
22
+ "application/json": {
23
+ schema: jwksKeySchema,
24
+ },
25
+ },
26
+ description: "List of tenants",
27
+ },
28
+ },
29
+ }),
30
+ async (ctx) => {
31
+ const { env } = ctx;
32
+
33
+ // const certificates = await env.data.keys.list();
34
+ // const keys = certificates.map((cert) => {
35
+ // const { alg, n, e, kty } = JSON.parse(cert.public_key);
36
+ // if (!alg || !e || !kty || !n) {
37
+ // throw new Error("Invalid public key");
38
+ // }
39
+
40
+ // return jwksSchema.parse({
41
+ // kid: cert.kid,
42
+ // alg,
43
+ // n,
44
+ // e,
45
+ // kty,
46
+ // });
47
+ // });
48
+
49
+ // TODO: This is a stub implementation. Replace with the actual implementation
50
+ const keys = [
51
+ {
52
+ alg: "RS256",
53
+ e: "AQAB",
54
+ kid: "hZ42TWGWLdmyKfwGVA6c2",
55
+ kty: "RSA",
56
+ n: "nUd-mktFZQNfVwmXufxcVcvJo6Lkb-jDuymtfQunmEhWCctOccWx9e7LX7_9uN15ZnRS7XJInPMRs9KLYdZ0GCnE2HM_QbrEoHpdkCRgyTE-KzmoaEv_AOVGE_Kg0-0ct3r9Z7aJLDVAsxXl1C9y8Gr7ZYkq0c4DyZr9VT8nQiwZQERbfxXdXw-5RLj21S_Lm-LL-AjKvry_TDBLpfUFJV18SVsM07lY_V45TwykNewRdaGLspFIeGdG5j5eByV8ifzBqvzOSptSCsmOTtW-ceWUk0FPD7g_KKzjjbzenoB0TC8mBb_4vWZlHnuGIAs2YoTFglp9uNu7t_OVl3Svo6ZE6alzUnaNfZNeAi78KPHYQ4tDWPjpYNfGynsiD0nojkDSPCIak56jWNYjj614cPEBiv9MVQRiSbBxpiGhMoHlW_QCCPMcXygLAaRs_tUksqoH4QB80krifG2yHPgGDPjXK1_0cYzV80iOcQIeoceqhkSSc6YxzzgDrQcsV2k3bizRQSL83GWkpdHhTZn-Q_JzsW_bDY_f9fjigYbRnoDSgS7038VFIPc92StE41MdgvIQMomcyEE4lYK1uv1Mo6cnXbCZhm8tvddo7VKNorOB4nsiv8DGrWPlzQBca9VN4C1oE2mH-3WLFR7XEkBHWVouOdTWM2S3K9F10YtahkM",
57
+ },
58
+ ];
59
+
60
+ return ctx.json(
61
+ { keys },
62
+ {
63
+ headers: {
64
+ "access-control-allow-origin": "*",
65
+ "access-control-allow-method": "GET",
66
+ "cache-control": `public, max-age=${env.JWKS_CACHE_TIMEOUT_IN_SECONDS}, stale-while-revalidate=${
67
+ env.JWKS_CACHE_TIMEOUT_IN_SECONDS * 2
68
+ }, stale-if-error=86400`,
69
+ },
70
+ },
71
+ );
72
+ },
73
+ )
74
+ // --------------------------------
75
+ // GET /.well-known/openid-configuration
76
+ // --------------------------------
77
+ .openapi(
78
+ createRoute({
79
+ tags: ["well known"],
80
+ method: "get",
81
+ path: "/openid-configuration",
82
+ request: {},
83
+ responses: {
84
+ 200: {
85
+ content: {
86
+ "application/json": {
87
+ schema: openIDConfigurationSchema,
88
+ },
89
+ },
90
+ description: "List of tenants",
91
+ },
92
+ },
93
+ }),
94
+ async (ctx) => {
95
+ const { env } = ctx;
96
+ const { ISSUER } = env;
97
+
98
+ const result = openIDConfigurationSchema.parse({
99
+ issuer: ISSUER,
100
+ authorization_endpoint: `${ISSUER}authorize`,
101
+ token_endpoint: `${ISSUER}oauth/token`,
102
+ device_authorization_endpoint: `${ISSUER}oauth/device/code`,
103
+ userinfo_endpoint: `${ISSUER}userinfo`,
104
+ mfa_challenge_endpoint: `${ISSUER}mfa/challenge`,
105
+ jwks_uri: `${ISSUER}.well-known/jwks.json`,
106
+ registration_endpoint: `${ISSUER}oidc/register`,
107
+ revocation_endpoint: `${ISSUER}oauth/revoke`,
108
+ scopes_supported: [
109
+ "openid",
110
+ "profile",
111
+ "offline_access",
112
+ "name",
113
+ "given_name",
114
+ "family_name",
115
+ "nickname",
116
+ "email",
117
+ "email_verified",
118
+ "picture",
119
+ "created_at",
120
+ "identities",
121
+ "phone",
122
+ "address",
123
+ ],
124
+ response_types_supported: [
125
+ "code",
126
+ "token",
127
+ "id_token",
128
+ "code token",
129
+ "code id_token",
130
+ "token id_token",
131
+ "code token id_token",
132
+ ],
133
+ code_challenge_methods_supported: ["S256", "plain"],
134
+ response_modes_supported: ["query", "fragment", "form_post"],
135
+ subject_types_supported: ["public"],
136
+ id_token_signing_alg_values_supported: ["HS256", "RS256"],
137
+ token_endpoint_auth_methods_supported: [
138
+ "client_secret_basic",
139
+ "client_secret_post",
140
+ "private_key_jwt",
141
+ ],
142
+ claims_supported: [
143
+ "aud",
144
+ "auth_time",
145
+ "created_at",
146
+ "email",
147
+ "email_verified",
148
+ "exp",
149
+ "family_name",
150
+ "given_name",
151
+ "iat",
152
+ "identities",
153
+ "iss",
154
+ "name",
155
+ "nickname",
156
+ "phone_number",
157
+ "picture",
158
+ "sub",
159
+ ],
160
+ request_uri_parameter_supported: false,
161
+ request_parameter_supported: false,
162
+ token_endpoint_auth_signing_alg_values_supported: [
163
+ "RS256",
164
+ "RS384",
165
+ "PS256",
166
+ ],
167
+ });
168
+
169
+ return ctx.json(result, {
170
+ headers: {
171
+ "access-control-allow-origin": "*",
172
+ "access-control-allow-method": "GET",
173
+ "cache-control": `public, max-age=${env.JWKS_CACHE_TIMEOUT_IN_SECONDS}, stale-while-revalidate=${
174
+ env.JWKS_CACHE_TIMEOUT_IN_SECONDS * 2
175
+ }, stale-if-error=86400`,
176
+ },
177
+ });
178
+ },
179
+ );
@@ -1,4 +1,7 @@
1
1
  export type Bindings = {
2
2
  ISSUER: string;
3
3
  ENVIRONMENT: string;
4
+
5
+ // Constants
6
+ JWKS_CACHE_TIMEOUT_IN_SECONDS: number;
4
7
  };
@@ -0,0 +1,37 @@
1
+ import { z } from "@hono/zod-openapi";
2
+
3
+ export const jwksSchema = z.object({
4
+ alg: z.string(),
5
+ e: z.string(),
6
+ kid: z.string(),
7
+ kty: z.string(),
8
+ n: z.string(),
9
+ use: z.string().optional(),
10
+ });
11
+
12
+ export const jwksKeySchema = z.object({
13
+ keys: z.array(jwksSchema),
14
+ });
15
+
16
+ export const openIDConfigurationSchema = z.object({
17
+ issuer: z.string(),
18
+ authorization_endpoint: z.string(),
19
+ token_endpoint: z.string(),
20
+ device_authorization_endpoint: z.string(),
21
+ userinfo_endpoint: z.string(),
22
+ mfa_challenge_endpoint: z.string(),
23
+ jwks_uri: z.string(),
24
+ registration_endpoint: z.string(),
25
+ revocation_endpoint: z.string(),
26
+ scopes_supported: z.array(z.string()),
27
+ response_types_supported: z.array(z.string()),
28
+ code_challenge_methods_supported: z.array(z.string()),
29
+ response_modes_supported: z.array(z.string()),
30
+ subject_types_supported: z.array(z.string()),
31
+ id_token_signing_alg_values_supported: z.array(z.string()),
32
+ token_endpoint_auth_methods_supported: z.array(z.string()),
33
+ claims_supported: z.array(z.string()),
34
+ request_uri_parameter_supported: z.boolean(),
35
+ request_parameter_supported: z.boolean(),
36
+ token_endpoint_auth_signing_alg_values_supported: z.array(z.string()),
37
+ });
@@ -0,0 +1 @@
1
+ export type Variables = {};
@@ -0,0 +1,3 @@
1
+ export * from "./Bindings";
2
+ export * from "./JWKS";
3
+ export * from "./Variables";
package/vite.config.ts CHANGED
@@ -1,11 +1,41 @@
1
+ import path from "path";
1
2
  import { defineConfig } from "vite";
2
- import { resolve } from "path";
3
- import dts from "vite-plugin-dts";
4
3
 
5
- // https://vitejs.dev/config/
6
- export default defineConfig({
4
+ const getPackageName = () => {
5
+ return "authhero";
6
+ };
7
+
8
+ const getPackageNameCamelCase = () => {
9
+ try {
10
+ return getPackageName().replace(/-./g, (char) => char[1].toUpperCase());
11
+ } catch (err) {
12
+ throw new Error("Name property in package.json is missing.");
13
+ }
14
+ };
15
+
16
+ const fileName = {
17
+ es: `${getPackageName()}.mjs`,
18
+ cjs: `${getPackageName()}.cjs`,
19
+ iife: `${getPackageName()}.iife.js`,
20
+ };
21
+
22
+ const formats = Object.keys(fileName) as Array<keyof typeof fileName>;
23
+
24
+ module.exports = defineConfig({
25
+ base: "./",
7
26
  build: {
8
- lib: { entry: resolve(__dirname, "src/index.ts"), formats: ["es"] },
27
+ outDir: "./build/dist",
28
+ lib: {
29
+ entry: path.resolve(__dirname, "src/index.ts"),
30
+ name: getPackageNameCamelCase(),
31
+ formats,
32
+ fileName: (format) => fileName[format],
33
+ },
34
+ },
35
+ resolve: {
36
+ alias: [
37
+ { find: "@", replacement: path.resolve(__dirname, "src") },
38
+ { find: "@@", replacement: path.resolve(__dirname) },
39
+ ],
9
40
  },
10
- plugins: [dts()],
11
41
  });