@_mustachio/openauth 0.6.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/dist/esm/client.js +186 -0
- package/dist/esm/css.d.js +0 -0
- package/dist/esm/error.js +73 -0
- package/dist/esm/index.js +14 -0
- package/dist/esm/issuer.js +558 -0
- package/dist/esm/jwt.js +16 -0
- package/dist/esm/keys.js +113 -0
- package/dist/esm/pkce.js +35 -0
- package/dist/esm/provider/apple.js +28 -0
- package/dist/esm/provider/arctic.js +43 -0
- package/dist/esm/provider/code.js +58 -0
- package/dist/esm/provider/cognito.js +16 -0
- package/dist/esm/provider/discord.js +15 -0
- package/dist/esm/provider/facebook.js +24 -0
- package/dist/esm/provider/github.js +15 -0
- package/dist/esm/provider/google.js +25 -0
- package/dist/esm/provider/index.js +3 -0
- package/dist/esm/provider/jumpcloud.js +15 -0
- package/dist/esm/provider/keycloak.js +15 -0
- package/dist/esm/provider/linkedin.js +15 -0
- package/dist/esm/provider/m2m.js +17 -0
- package/dist/esm/provider/microsoft.js +24 -0
- package/dist/esm/provider/oauth2.js +119 -0
- package/dist/esm/provider/oidc.js +69 -0
- package/dist/esm/provider/passkey.js +315 -0
- package/dist/esm/provider/password.js +306 -0
- package/dist/esm/provider/provider.js +10 -0
- package/dist/esm/provider/slack.js +15 -0
- package/dist/esm/provider/spotify.js +15 -0
- package/dist/esm/provider/twitch.js +15 -0
- package/dist/esm/provider/x.js +16 -0
- package/dist/esm/provider/yahoo.js +15 -0
- package/dist/esm/random.js +27 -0
- package/dist/esm/storage/aws.js +39 -0
- package/dist/esm/storage/cloudflare.js +42 -0
- package/dist/esm/storage/dynamo.js +116 -0
- package/dist/esm/storage/memory.js +88 -0
- package/dist/esm/storage/storage.js +36 -0
- package/dist/esm/subject.js +7 -0
- package/dist/esm/ui/base.js +407 -0
- package/dist/esm/ui/code.js +151 -0
- package/dist/esm/ui/form.js +43 -0
- package/dist/esm/ui/icon.js +92 -0
- package/dist/esm/ui/passkey.js +329 -0
- package/dist/esm/ui/password.js +338 -0
- package/dist/esm/ui/select.js +187 -0
- package/dist/esm/ui/theme.js +115 -0
- package/dist/esm/util.js +54 -0
- package/dist/types/client.d.ts +466 -0
- package/dist/types/client.d.ts.map +1 -0
- package/dist/types/error.d.ts +77 -0
- package/dist/types/error.d.ts.map +1 -0
- package/dist/types/index.d.ts +20 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/issuer.d.ts +465 -0
- package/dist/types/issuer.d.ts.map +1 -0
- package/dist/types/jwt.d.ts +6 -0
- package/dist/types/jwt.d.ts.map +1 -0
- package/dist/types/keys.d.ts +18 -0
- package/dist/types/keys.d.ts.map +1 -0
- package/dist/types/pkce.d.ts +7 -0
- package/dist/types/pkce.d.ts.map +1 -0
- package/dist/types/provider/apple.d.ts +108 -0
- package/dist/types/provider/apple.d.ts.map +1 -0
- package/dist/types/provider/arctic.d.ts +16 -0
- package/dist/types/provider/arctic.d.ts.map +1 -0
- package/dist/types/provider/code.d.ts +74 -0
- package/dist/types/provider/code.d.ts.map +1 -0
- package/dist/types/provider/cognito.d.ts +64 -0
- package/dist/types/provider/cognito.d.ts.map +1 -0
- package/dist/types/provider/discord.d.ts +38 -0
- package/dist/types/provider/discord.d.ts.map +1 -0
- package/dist/types/provider/facebook.d.ts +74 -0
- package/dist/types/provider/facebook.d.ts.map +1 -0
- package/dist/types/provider/github.d.ts +38 -0
- package/dist/types/provider/github.d.ts.map +1 -0
- package/dist/types/provider/google.d.ts +74 -0
- package/dist/types/provider/google.d.ts.map +1 -0
- package/dist/types/provider/index.d.ts +4 -0
- package/dist/types/provider/index.d.ts.map +1 -0
- package/dist/types/provider/jumpcloud.d.ts +38 -0
- package/dist/types/provider/jumpcloud.d.ts.map +1 -0
- package/dist/types/provider/keycloak.d.ts +67 -0
- package/dist/types/provider/keycloak.d.ts.map +1 -0
- package/dist/types/provider/linkedin.d.ts +6 -0
- package/dist/types/provider/linkedin.d.ts.map +1 -0
- package/dist/types/provider/m2m.d.ts +34 -0
- package/dist/types/provider/m2m.d.ts.map +1 -0
- package/dist/types/provider/microsoft.d.ts +89 -0
- package/dist/types/provider/microsoft.d.ts.map +1 -0
- package/dist/types/provider/oauth2.d.ts +133 -0
- package/dist/types/provider/oauth2.d.ts.map +1 -0
- package/dist/types/provider/oidc.d.ts +91 -0
- package/dist/types/provider/oidc.d.ts.map +1 -0
- package/dist/types/provider/passkey.d.ts +143 -0
- package/dist/types/provider/passkey.d.ts.map +1 -0
- package/dist/types/provider/password.d.ts +210 -0
- package/dist/types/provider/password.d.ts.map +1 -0
- package/dist/types/provider/provider.d.ts +29 -0
- package/dist/types/provider/provider.d.ts.map +1 -0
- package/dist/types/provider/slack.d.ts +59 -0
- package/dist/types/provider/slack.d.ts.map +1 -0
- package/dist/types/provider/spotify.d.ts +38 -0
- package/dist/types/provider/spotify.d.ts.map +1 -0
- package/dist/types/provider/twitch.d.ts +38 -0
- package/dist/types/provider/twitch.d.ts.map +1 -0
- package/dist/types/provider/x.d.ts +38 -0
- package/dist/types/provider/x.d.ts.map +1 -0
- package/dist/types/provider/yahoo.d.ts +38 -0
- package/dist/types/provider/yahoo.d.ts.map +1 -0
- package/dist/types/random.d.ts +3 -0
- package/dist/types/random.d.ts.map +1 -0
- package/dist/types/storage/aws.d.ts +4 -0
- package/dist/types/storage/aws.d.ts.map +1 -0
- package/dist/types/storage/cloudflare.d.ts +34 -0
- package/dist/types/storage/cloudflare.d.ts.map +1 -0
- package/dist/types/storage/dynamo.d.ts +65 -0
- package/dist/types/storage/dynamo.d.ts.map +1 -0
- package/dist/types/storage/memory.d.ts +49 -0
- package/dist/types/storage/memory.d.ts.map +1 -0
- package/dist/types/storage/storage.d.ts +15 -0
- package/dist/types/storage/storage.d.ts.map +1 -0
- package/dist/types/subject.d.ts +122 -0
- package/dist/types/subject.d.ts.map +1 -0
- package/dist/types/ui/base.d.ts +5 -0
- package/dist/types/ui/base.d.ts.map +1 -0
- package/dist/types/ui/code.d.ts +104 -0
- package/dist/types/ui/code.d.ts.map +1 -0
- package/dist/types/ui/form.d.ts +6 -0
- package/dist/types/ui/form.d.ts.map +1 -0
- package/dist/types/ui/icon.d.ts +6 -0
- package/dist/types/ui/icon.d.ts.map +1 -0
- package/dist/types/ui/passkey.d.ts +5 -0
- package/dist/types/ui/passkey.d.ts.map +1 -0
- package/dist/types/ui/password.d.ts +139 -0
- package/dist/types/ui/password.d.ts.map +1 -0
- package/dist/types/ui/select.d.ts +55 -0
- package/dist/types/ui/select.d.ts.map +1 -0
- package/dist/types/ui/theme.d.ts +207 -0
- package/dist/types/ui/theme.d.ts.map +1 -0
- package/dist/types/util.d.ts +8 -0
- package/dist/types/util.d.ts.map +1 -0
- package/package.json +51 -0
- package/src/client.ts +749 -0
- package/src/css.d.ts +4 -0
- package/src/error.ts +120 -0
- package/src/index.ts +26 -0
- package/src/issuer.ts +1302 -0
- package/src/jwt.ts +17 -0
- package/src/keys.ts +139 -0
- package/src/pkce.ts +40 -0
- package/src/provider/apple.ts +127 -0
- package/src/provider/arctic.ts +66 -0
- package/src/provider/code.ts +227 -0
- package/src/provider/cognito.ts +74 -0
- package/src/provider/discord.ts +45 -0
- package/src/provider/facebook.ts +84 -0
- package/src/provider/github.ts +45 -0
- package/src/provider/google.ts +85 -0
- package/src/provider/index.ts +3 -0
- package/src/provider/jumpcloud.ts +45 -0
- package/src/provider/keycloak.ts +75 -0
- package/src/provider/linkedin.ts +12 -0
- package/src/provider/m2m.ts +56 -0
- package/src/provider/microsoft.ts +100 -0
- package/src/provider/oauth2.ts +297 -0
- package/src/provider/oidc.ts +179 -0
- package/src/provider/passkey.ts +655 -0
- package/src/provider/password.ts +672 -0
- package/src/provider/provider.ts +33 -0
- package/src/provider/slack.ts +67 -0
- package/src/provider/spotify.ts +45 -0
- package/src/provider/twitch.ts +45 -0
- package/src/provider/x.ts +46 -0
- package/src/provider/yahoo.ts +45 -0
- package/src/random.ts +24 -0
- package/src/storage/aws.ts +59 -0
- package/src/storage/cloudflare.ts +77 -0
- package/src/storage/dynamo.ts +193 -0
- package/src/storage/memory.ts +135 -0
- package/src/storage/storage.ts +46 -0
- package/src/subject.ts +130 -0
- package/src/ui/base.tsx +118 -0
- package/src/ui/code.tsx +215 -0
- package/src/ui/form.tsx +40 -0
- package/src/ui/icon.tsx +95 -0
- package/src/ui/passkey.tsx +321 -0
- package/src/ui/password.tsx +405 -0
- package/src/ui/select.tsx +221 -0
- package/src/ui/theme.ts +319 -0
- package/src/ui/ui.css +252 -0
- package/src/util.ts +58 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
// src/client.ts
|
|
2
|
+
import {
|
|
3
|
+
createLocalJWKSet,
|
|
4
|
+
errors,
|
|
5
|
+
jwtVerify,
|
|
6
|
+
decodeJwt
|
|
7
|
+
} from "jose";
|
|
8
|
+
import {
|
|
9
|
+
InvalidAccessTokenError,
|
|
10
|
+
InvalidAuthorizationCodeError,
|
|
11
|
+
InvalidRefreshTokenError,
|
|
12
|
+
InvalidSubjectError
|
|
13
|
+
} from "./error.js";
|
|
14
|
+
import { generatePKCE } from "./pkce.js";
|
|
15
|
+
function createClient(input) {
|
|
16
|
+
const jwksCache = new Map;
|
|
17
|
+
const issuerCache = new Map;
|
|
18
|
+
const issuer = input.issuer || process.env.OPENAUTH_ISSUER;
|
|
19
|
+
if (!issuer)
|
|
20
|
+
throw new Error("No issuer");
|
|
21
|
+
const f = input.fetch ?? fetch;
|
|
22
|
+
async function getIssuer() {
|
|
23
|
+
const cached = issuerCache.get(issuer);
|
|
24
|
+
if (cached)
|
|
25
|
+
return cached;
|
|
26
|
+
const wellKnown = await (f || fetch)(`${issuer}/.well-known/oauth-authorization-server`).then((r) => r.json());
|
|
27
|
+
issuerCache.set(issuer, wellKnown);
|
|
28
|
+
return wellKnown;
|
|
29
|
+
}
|
|
30
|
+
async function getJWKS() {
|
|
31
|
+
const wk = await getIssuer();
|
|
32
|
+
const cached = jwksCache.get(issuer);
|
|
33
|
+
if (cached)
|
|
34
|
+
return cached;
|
|
35
|
+
const keyset = await (f || fetch)(wk.jwks_uri).then((r) => r.json());
|
|
36
|
+
const result2 = createLocalJWKSet(keyset);
|
|
37
|
+
jwksCache.set(issuer, result2);
|
|
38
|
+
return result2;
|
|
39
|
+
}
|
|
40
|
+
const result = {
|
|
41
|
+
async authorize(redirectURI, response, opts) {
|
|
42
|
+
const result2 = new URL(issuer + "/authorize");
|
|
43
|
+
const challenge = {
|
|
44
|
+
state: crypto.randomUUID()
|
|
45
|
+
};
|
|
46
|
+
result2.searchParams.set("client_id", input.clientID);
|
|
47
|
+
result2.searchParams.set("redirect_uri", redirectURI);
|
|
48
|
+
result2.searchParams.set("response_type", response);
|
|
49
|
+
result2.searchParams.set("state", challenge.state);
|
|
50
|
+
if (opts?.provider)
|
|
51
|
+
result2.searchParams.set("provider", opts.provider);
|
|
52
|
+
if (opts?.pkce && response === "code") {
|
|
53
|
+
const pkce = await generatePKCE();
|
|
54
|
+
result2.searchParams.set("code_challenge_method", "S256");
|
|
55
|
+
result2.searchParams.set("code_challenge", pkce.challenge);
|
|
56
|
+
challenge.verifier = pkce.verifier;
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
challenge,
|
|
60
|
+
url: result2.toString()
|
|
61
|
+
};
|
|
62
|
+
},
|
|
63
|
+
async pkce(redirectURI, opts) {
|
|
64
|
+
const result2 = new URL(issuer + "/authorize");
|
|
65
|
+
if (opts?.provider)
|
|
66
|
+
result2.searchParams.set("provider", opts.provider);
|
|
67
|
+
result2.searchParams.set("client_id", input.clientID);
|
|
68
|
+
result2.searchParams.set("redirect_uri", redirectURI);
|
|
69
|
+
result2.searchParams.set("response_type", "code");
|
|
70
|
+
const pkce = await generatePKCE();
|
|
71
|
+
result2.searchParams.set("code_challenge_method", "S256");
|
|
72
|
+
result2.searchParams.set("code_challenge", pkce.challenge);
|
|
73
|
+
return [pkce.verifier, result2.toString()];
|
|
74
|
+
},
|
|
75
|
+
async exchange(code, redirectURI, verifier) {
|
|
76
|
+
const tokens = await f(issuer + "/token", {
|
|
77
|
+
method: "POST",
|
|
78
|
+
headers: {
|
|
79
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
80
|
+
},
|
|
81
|
+
body: new URLSearchParams({
|
|
82
|
+
code,
|
|
83
|
+
redirect_uri: redirectURI,
|
|
84
|
+
grant_type: "authorization_code",
|
|
85
|
+
client_id: input.clientID,
|
|
86
|
+
code_verifier: verifier || ""
|
|
87
|
+
}).toString()
|
|
88
|
+
});
|
|
89
|
+
const json = await tokens.json();
|
|
90
|
+
if (!tokens.ok) {
|
|
91
|
+
return {
|
|
92
|
+
err: new InvalidAuthorizationCodeError
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
err: false,
|
|
97
|
+
tokens: {
|
|
98
|
+
access: json.access_token,
|
|
99
|
+
refresh: json.refresh_token,
|
|
100
|
+
expiresIn: json.expires_in
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
},
|
|
104
|
+
async refresh(refresh, opts) {
|
|
105
|
+
if (opts && opts.access) {
|
|
106
|
+
const decoded = decodeJwt(opts.access);
|
|
107
|
+
if (!decoded) {
|
|
108
|
+
return {
|
|
109
|
+
err: new InvalidAccessTokenError
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
if ((decoded.exp || 0) > Date.now() / 1000 + 30) {
|
|
113
|
+
return {
|
|
114
|
+
err: false
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
const tokens = await f(issuer + "/token", {
|
|
119
|
+
method: "POST",
|
|
120
|
+
headers: {
|
|
121
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
122
|
+
},
|
|
123
|
+
body: new URLSearchParams({
|
|
124
|
+
grant_type: "refresh_token",
|
|
125
|
+
refresh_token: refresh
|
|
126
|
+
}).toString()
|
|
127
|
+
});
|
|
128
|
+
const json = await tokens.json();
|
|
129
|
+
if (!tokens.ok) {
|
|
130
|
+
return {
|
|
131
|
+
err: new InvalidRefreshTokenError
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
err: false,
|
|
136
|
+
tokens: {
|
|
137
|
+
access: json.access_token,
|
|
138
|
+
refresh: json.refresh_token,
|
|
139
|
+
expiresIn: json.expires_in
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
},
|
|
143
|
+
async verify(subjects, token, options) {
|
|
144
|
+
const jwks = await getJWKS();
|
|
145
|
+
try {
|
|
146
|
+
const result2 = await jwtVerify(token, jwks, {
|
|
147
|
+
issuer
|
|
148
|
+
});
|
|
149
|
+
const validated = await subjects[result2.payload.type]["~standard"].validate(result2.payload.properties);
|
|
150
|
+
if (!validated.issues && result2.payload.mode === "access")
|
|
151
|
+
return {
|
|
152
|
+
aud: result2.payload.aud,
|
|
153
|
+
subject: {
|
|
154
|
+
type: result2.payload.type,
|
|
155
|
+
properties: validated.value
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
return {
|
|
159
|
+
err: new InvalidSubjectError
|
|
160
|
+
};
|
|
161
|
+
} catch (e) {
|
|
162
|
+
if (e instanceof errors.JWTExpired && options?.refresh) {
|
|
163
|
+
const refreshed = await this.refresh(options.refresh);
|
|
164
|
+
if (refreshed.err)
|
|
165
|
+
return refreshed;
|
|
166
|
+
const verified = await result.verify(subjects, refreshed.tokens.access, {
|
|
167
|
+
refresh: refreshed.tokens.refresh,
|
|
168
|
+
issuer,
|
|
169
|
+
fetch: options?.fetch
|
|
170
|
+
});
|
|
171
|
+
if (verified.err)
|
|
172
|
+
return verified;
|
|
173
|
+
verified.tokens = refreshed.tokens;
|
|
174
|
+
return verified;
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
err: new InvalidAccessTokenError
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
return result;
|
|
183
|
+
}
|
|
184
|
+
export {
|
|
185
|
+
createClient
|
|
186
|
+
};
|
|
File without changes
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// src/error.ts
|
|
2
|
+
class OauthError extends Error {
|
|
3
|
+
error;
|
|
4
|
+
description;
|
|
5
|
+
constructor(error, description) {
|
|
6
|
+
super(error + " - " + description);
|
|
7
|
+
this.error = error;
|
|
8
|
+
this.description = description;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
class MissingProviderError extends OauthError {
|
|
13
|
+
constructor() {
|
|
14
|
+
super("invalid_request", "Must specify `provider` query parameter if `select` callback on issuer is not specified");
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
class MissingParameterError extends OauthError {
|
|
19
|
+
parameter;
|
|
20
|
+
constructor(parameter) {
|
|
21
|
+
super("invalid_request", "Missing parameter: " + parameter);
|
|
22
|
+
this.parameter = parameter;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
class UnauthorizedClientError extends OauthError {
|
|
27
|
+
clientID;
|
|
28
|
+
constructor(clientID, redirectURI) {
|
|
29
|
+
super("unauthorized_client", `Client ${clientID} is not authorized to use this redirect_uri: ${redirectURI}`);
|
|
30
|
+
this.clientID = clientID;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
class UnknownStateError extends Error {
|
|
35
|
+
constructor() {
|
|
36
|
+
super("The browser was in an unknown state. This could be because certain cookies expired or the browser was switched in the middle of an authentication flow.");
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
class InvalidSubjectError extends Error {
|
|
41
|
+
constructor() {
|
|
42
|
+
super("Invalid subject");
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
class InvalidRefreshTokenError extends Error {
|
|
47
|
+
constructor() {
|
|
48
|
+
super("Invalid refresh token");
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
class InvalidAccessTokenError extends Error {
|
|
53
|
+
constructor() {
|
|
54
|
+
super("Invalid access token");
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
class InvalidAuthorizationCodeError extends Error {
|
|
59
|
+
constructor() {
|
|
60
|
+
super("Invalid authorization code");
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
export {
|
|
64
|
+
UnknownStateError,
|
|
65
|
+
UnauthorizedClientError,
|
|
66
|
+
OauthError,
|
|
67
|
+
MissingProviderError,
|
|
68
|
+
MissingParameterError,
|
|
69
|
+
InvalidSubjectError,
|
|
70
|
+
InvalidRefreshTokenError,
|
|
71
|
+
InvalidAuthorizationCodeError,
|
|
72
|
+
InvalidAccessTokenError
|
|
73
|
+
};
|