authhero 0.2.3 → 0.2.4
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/dist/authhero.cjs +1 -1
- package/dist/authhero.d.ts +2684 -0
- package/dist/authhero.iife.js +1 -1
- package/dist/authhero.mjs +694 -655
- package/package.json +8 -4
- package/CHANGELOG.md +0 -34
- package/src/hooks/index.ts +0 -29
- package/src/hooks/link-users.ts +0 -31
- package/src/hooks/webhooks.ts +0 -77
- package/src/index.ts +0 -41
- package/src/management-app.ts +0 -55
- package/src/middlewares/register-component.ts +0 -36
- package/src/routes/management-api/tenants.ts +0 -224
- package/src/routes/oauth2/index.ts +0 -1
- package/src/routes/oauth2/well-known.ts +0 -178
- package/src/types/Bindings.ts +0 -12
- package/src/types/Query.ts +0 -42
- package/src/types/Variables.ts +0 -3
- package/src/types/index.ts +0 -3
- package/src/utils/parse-sort.ts +0 -18
- package/src/utils/users.ts +0 -130
- package/src/vite-env.d.ts +0 -1
- package/tsconfig.json +0 -11
- package/tsconfig.node.json +0 -4
- package/vite.config.ts +0 -41
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
jwksKeySchema,
|
|
3
|
-
openIDConfigurationSchema,
|
|
4
|
-
} from "@authhero/adapter-interfaces";
|
|
5
|
-
import { Bindings } from "../../types";
|
|
6
|
-
import { OpenAPIHono, createRoute } from "@hono/zod-openapi";
|
|
7
|
-
|
|
8
|
-
export const wellKnownRoutes = new OpenAPIHono<{ Bindings: Bindings }>()
|
|
9
|
-
// --------------------------------
|
|
10
|
-
// GET /.well-known/jwks.json
|
|
11
|
-
// --------------------------------
|
|
12
|
-
.openapi(
|
|
13
|
-
createRoute({
|
|
14
|
-
tags: ["jwks"],
|
|
15
|
-
method: "get",
|
|
16
|
-
path: "/jwks.json",
|
|
17
|
-
request: {},
|
|
18
|
-
responses: {
|
|
19
|
-
200: {
|
|
20
|
-
content: {
|
|
21
|
-
"application/json": {
|
|
22
|
-
schema: jwksKeySchema,
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
description: "List of tenants",
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
}),
|
|
29
|
-
async (ctx) => {
|
|
30
|
-
const { env } = ctx;
|
|
31
|
-
|
|
32
|
-
// const certificates = await env.data.keys.list();
|
|
33
|
-
// const keys = certificates.map((cert) => {
|
|
34
|
-
// const { alg, n, e, kty } = JSON.parse(cert.public_key);
|
|
35
|
-
// if (!alg || !e || !kty || !n) {
|
|
36
|
-
// throw new Error("Invalid public key");
|
|
37
|
-
// }
|
|
38
|
-
|
|
39
|
-
// return jwksSchema.parse({
|
|
40
|
-
// kid: cert.kid,
|
|
41
|
-
// alg,
|
|
42
|
-
// n,
|
|
43
|
-
// e,
|
|
44
|
-
// kty,
|
|
45
|
-
// });
|
|
46
|
-
// });
|
|
47
|
-
|
|
48
|
-
// TODO: This is a stub implementation. Replace with the actual implementation
|
|
49
|
-
const keys = [
|
|
50
|
-
{
|
|
51
|
-
alg: "RS256",
|
|
52
|
-
e: "AQAB",
|
|
53
|
-
kid: "hZ42TWGWLdmyKfwGVA6c2",
|
|
54
|
-
kty: "RSA",
|
|
55
|
-
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",
|
|
56
|
-
},
|
|
57
|
-
];
|
|
58
|
-
|
|
59
|
-
return ctx.json(
|
|
60
|
-
{ keys },
|
|
61
|
-
{
|
|
62
|
-
headers: {
|
|
63
|
-
"access-control-allow-origin": "*",
|
|
64
|
-
"access-control-allow-method": "GET",
|
|
65
|
-
"cache-control": `public, max-age=${env.JWKS_CACHE_TIMEOUT_IN_SECONDS}, stale-while-revalidate=${
|
|
66
|
-
env.JWKS_CACHE_TIMEOUT_IN_SECONDS * 2
|
|
67
|
-
}, stale-if-error=86400`,
|
|
68
|
-
},
|
|
69
|
-
},
|
|
70
|
-
);
|
|
71
|
-
},
|
|
72
|
-
)
|
|
73
|
-
// --------------------------------
|
|
74
|
-
// GET /.well-known/openid-configuration
|
|
75
|
-
// --------------------------------
|
|
76
|
-
.openapi(
|
|
77
|
-
createRoute({
|
|
78
|
-
tags: ["well known"],
|
|
79
|
-
method: "get",
|
|
80
|
-
path: "/openid-configuration",
|
|
81
|
-
request: {},
|
|
82
|
-
responses: {
|
|
83
|
-
200: {
|
|
84
|
-
content: {
|
|
85
|
-
"application/json": {
|
|
86
|
-
schema: openIDConfigurationSchema,
|
|
87
|
-
},
|
|
88
|
-
},
|
|
89
|
-
description: "List of tenants",
|
|
90
|
-
},
|
|
91
|
-
},
|
|
92
|
-
}),
|
|
93
|
-
async (ctx) => {
|
|
94
|
-
const { env } = ctx;
|
|
95
|
-
const { ISSUER } = env;
|
|
96
|
-
|
|
97
|
-
const result = openIDConfigurationSchema.parse({
|
|
98
|
-
issuer: ISSUER,
|
|
99
|
-
authorization_endpoint: `${ISSUER}authorize`,
|
|
100
|
-
token_endpoint: `${ISSUER}oauth/token`,
|
|
101
|
-
device_authorization_endpoint: `${ISSUER}oauth/device/code`,
|
|
102
|
-
userinfo_endpoint: `${ISSUER}userinfo`,
|
|
103
|
-
mfa_challenge_endpoint: `${ISSUER}mfa/challenge`,
|
|
104
|
-
jwks_uri: `${ISSUER}.well-known/jwks.json`,
|
|
105
|
-
registration_endpoint: `${ISSUER}oidc/register`,
|
|
106
|
-
revocation_endpoint: `${ISSUER}oauth/revoke`,
|
|
107
|
-
scopes_supported: [
|
|
108
|
-
"openid",
|
|
109
|
-
"profile",
|
|
110
|
-
"offline_access",
|
|
111
|
-
"name",
|
|
112
|
-
"given_name",
|
|
113
|
-
"family_name",
|
|
114
|
-
"nickname",
|
|
115
|
-
"email",
|
|
116
|
-
"email_verified",
|
|
117
|
-
"picture",
|
|
118
|
-
"created_at",
|
|
119
|
-
"identities",
|
|
120
|
-
"phone",
|
|
121
|
-
"address",
|
|
122
|
-
],
|
|
123
|
-
response_types_supported: [
|
|
124
|
-
"code",
|
|
125
|
-
"token",
|
|
126
|
-
"id_token",
|
|
127
|
-
"code token",
|
|
128
|
-
"code id_token",
|
|
129
|
-
"token id_token",
|
|
130
|
-
"code token id_token",
|
|
131
|
-
],
|
|
132
|
-
code_challenge_methods_supported: ["S256", "plain"],
|
|
133
|
-
response_modes_supported: ["query", "fragment", "form_post"],
|
|
134
|
-
subject_types_supported: ["public"],
|
|
135
|
-
id_token_signing_alg_values_supported: ["HS256", "RS256"],
|
|
136
|
-
token_endpoint_auth_methods_supported: [
|
|
137
|
-
"client_secret_basic",
|
|
138
|
-
"client_secret_post",
|
|
139
|
-
"private_key_jwt",
|
|
140
|
-
],
|
|
141
|
-
claims_supported: [
|
|
142
|
-
"aud",
|
|
143
|
-
"auth_time",
|
|
144
|
-
"created_at",
|
|
145
|
-
"email",
|
|
146
|
-
"email_verified",
|
|
147
|
-
"exp",
|
|
148
|
-
"family_name",
|
|
149
|
-
"given_name",
|
|
150
|
-
"iat",
|
|
151
|
-
"identities",
|
|
152
|
-
"iss",
|
|
153
|
-
"name",
|
|
154
|
-
"nickname",
|
|
155
|
-
"phone_number",
|
|
156
|
-
"picture",
|
|
157
|
-
"sub",
|
|
158
|
-
],
|
|
159
|
-
request_uri_parameter_supported: false,
|
|
160
|
-
request_parameter_supported: false,
|
|
161
|
-
token_endpoint_auth_signing_alg_values_supported: [
|
|
162
|
-
"RS256",
|
|
163
|
-
"RS384",
|
|
164
|
-
"PS256",
|
|
165
|
-
],
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
return ctx.json(result, {
|
|
169
|
-
headers: {
|
|
170
|
-
"access-control-allow-origin": "*",
|
|
171
|
-
"access-control-allow-method": "GET",
|
|
172
|
-
"cache-control": `public, max-age=${env.JWKS_CACHE_TIMEOUT_IN_SECONDS}, stale-while-revalidate=${
|
|
173
|
-
env.JWKS_CACHE_TIMEOUT_IN_SECONDS * 2
|
|
174
|
-
}, stale-if-error=86400`,
|
|
175
|
-
},
|
|
176
|
-
});
|
|
177
|
-
},
|
|
178
|
-
);
|
package/src/types/Bindings.ts
DELETED
package/src/types/Query.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { z } from "@hono/zod-openapi";
|
|
2
|
-
|
|
3
|
-
export const querySchema = z.object({
|
|
4
|
-
page: z
|
|
5
|
-
.string()
|
|
6
|
-
.min(0)
|
|
7
|
-
.optional()
|
|
8
|
-
.default("0")
|
|
9
|
-
.transform((p) => parseInt(p, 10))
|
|
10
|
-
.openapi({
|
|
11
|
-
description: "The page number where 0 is the first page",
|
|
12
|
-
}),
|
|
13
|
-
per_page: z
|
|
14
|
-
.string()
|
|
15
|
-
.min(1)
|
|
16
|
-
.optional()
|
|
17
|
-
.default("10")
|
|
18
|
-
.transform((p) => parseInt(p, 10))
|
|
19
|
-
.openapi({
|
|
20
|
-
description: "The number of items per page",
|
|
21
|
-
}),
|
|
22
|
-
include_totals: z
|
|
23
|
-
.string()
|
|
24
|
-
.optional()
|
|
25
|
-
.default("false")
|
|
26
|
-
.transform((it) => it === "true")
|
|
27
|
-
.openapi({
|
|
28
|
-
description:
|
|
29
|
-
"If the total number of items should be included in the response",
|
|
30
|
-
}),
|
|
31
|
-
sort: z
|
|
32
|
-
.string()
|
|
33
|
-
.regex(/^.+:(-1|1)$/)
|
|
34
|
-
.optional()
|
|
35
|
-
.openapi({
|
|
36
|
-
description:
|
|
37
|
-
"A property that should have the format 'string:-1' or 'string:1'",
|
|
38
|
-
}),
|
|
39
|
-
q: z.string().optional().openapi({
|
|
40
|
-
description: "A lucene query string used to filter the results",
|
|
41
|
-
}),
|
|
42
|
-
});
|
package/src/types/Variables.ts
DELETED
package/src/types/index.ts
DELETED
package/src/utils/parse-sort.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
export function parseSort(sort?: string):
|
|
2
|
-
| undefined
|
|
3
|
-
| {
|
|
4
|
-
sort_by: string;
|
|
5
|
-
sort_order: "asc" | "desc";
|
|
6
|
-
} {
|
|
7
|
-
if (!sort) {
|
|
8
|
-
return undefined;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const [sort_by = "", orderString] = sort.split(":");
|
|
12
|
-
const sort_order = orderString === "1" ? "asc" : "desc";
|
|
13
|
-
|
|
14
|
-
return {
|
|
15
|
-
sort_by,
|
|
16
|
-
sort_order,
|
|
17
|
-
};
|
|
18
|
-
}
|
package/src/utils/users.ts
DELETED
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
import { User, UserDataAdapter } from "@authhero/adapter-interfaces";
|
|
2
|
-
|
|
3
|
-
export async function getUsersByEmail(
|
|
4
|
-
userAdapter: UserDataAdapter,
|
|
5
|
-
tenantId: string,
|
|
6
|
-
email: string,
|
|
7
|
-
): Promise<User[]> {
|
|
8
|
-
const response = await userAdapter.list(tenantId, {
|
|
9
|
-
page: 0,
|
|
10
|
-
per_page: 10,
|
|
11
|
-
include_totals: false,
|
|
12
|
-
q: `email:${email}`,
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
return response.users;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
interface GetUserByEmailAndProviderParams {
|
|
19
|
-
userAdapter: UserDataAdapter;
|
|
20
|
-
tenant_id: string;
|
|
21
|
-
email: string;
|
|
22
|
-
provider: string;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export async function getUserByEmailAndProvider({
|
|
26
|
-
userAdapter,
|
|
27
|
-
tenant_id,
|
|
28
|
-
email,
|
|
29
|
-
provider,
|
|
30
|
-
}: GetUserByEmailAndProviderParams): Promise<User | null> {
|
|
31
|
-
const { users } = await userAdapter.list(tenant_id, {
|
|
32
|
-
page: 0,
|
|
33
|
-
per_page: 10,
|
|
34
|
-
include_totals: false,
|
|
35
|
-
q: `email:${email} provider:${provider}`,
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
if (users.length > 1) {
|
|
39
|
-
console.error("More than one user found for same email and provider");
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return users[0] || null;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
interface GetPrimaryUserByEmailParams {
|
|
46
|
-
userAdapter: UserDataAdapter;
|
|
47
|
-
tenant_id: string;
|
|
48
|
-
email: string;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export async function getPrimaryUserByEmail({
|
|
52
|
-
userAdapter,
|
|
53
|
-
tenant_id,
|
|
54
|
-
email,
|
|
55
|
-
}: GetPrimaryUserByEmailParams): Promise<User | undefined> {
|
|
56
|
-
const { users: usersWithUnverifiedPasswordAccounts } = await userAdapter.list(
|
|
57
|
-
tenant_id,
|
|
58
|
-
{
|
|
59
|
-
page: 0,
|
|
60
|
-
per_page: 10,
|
|
61
|
-
include_totals: false,
|
|
62
|
-
q: `email:${email}`,
|
|
63
|
-
},
|
|
64
|
-
);
|
|
65
|
-
|
|
66
|
-
// filter out unverified accounts
|
|
67
|
-
const users = usersWithUnverifiedPasswordAccounts.filter(
|
|
68
|
-
// maybe we should do this for all providers
|
|
69
|
-
(user) => !(user.provider === "auth2" && !user.email_verified),
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
if (users.length === 0) {
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const primaryUsers = users.filter((user) => !user.linked_to);
|
|
77
|
-
|
|
78
|
-
if (primaryUsers.length > 0) {
|
|
79
|
-
if (primaryUsers.length > 1) {
|
|
80
|
-
console.error("More than one primary user found for same email");
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return primaryUsers[0];
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// so now we have only linked users for this email address
|
|
87
|
-
|
|
88
|
-
// I am going to assume that all the linked users with the same email address
|
|
89
|
-
// are linked to the same primary account
|
|
90
|
-
|
|
91
|
-
const primaryAccount = await userAdapter.get(tenant_id, users[0]?.linked_to!);
|
|
92
|
-
|
|
93
|
-
if (!primaryAccount) {
|
|
94
|
-
// this is a real error where we should interrupt the flow
|
|
95
|
-
throw new Error("Primary account not found");
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return primaryAccount;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
interface GetPrimaryUserByEmailAndProviderParams {
|
|
102
|
-
userAdapter: UserDataAdapter;
|
|
103
|
-
tenant_id: string;
|
|
104
|
-
email: string;
|
|
105
|
-
provider: string;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
export async function getPrimaryUserByEmailAndProvider({
|
|
109
|
-
userAdapter,
|
|
110
|
-
tenant_id,
|
|
111
|
-
email,
|
|
112
|
-
provider,
|
|
113
|
-
}: GetPrimaryUserByEmailAndProviderParams): Promise<User | null> {
|
|
114
|
-
const user = await getUserByEmailAndProvider({
|
|
115
|
-
userAdapter,
|
|
116
|
-
tenant_id,
|
|
117
|
-
email,
|
|
118
|
-
provider,
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
if (!user) {
|
|
122
|
-
return null;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (!user.linked_to) {
|
|
126
|
-
return user;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return userAdapter.get(tenant_id, user.linked_to);
|
|
130
|
-
}
|
package/src/vite-env.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
/// <reference types="vite/client" />
|
package/tsconfig.json
DELETED
package/tsconfig.node.json
DELETED
package/vite.config.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import path from "path";
|
|
2
|
-
import { defineConfig } from "vite";
|
|
3
|
-
|
|
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: "./",
|
|
26
|
-
build: {
|
|
27
|
-
outDir: "./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
|
-
],
|
|
40
|
-
},
|
|
41
|
-
});
|