sst 2.26.6 → 2.26.8
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/cli/commands/bind.js +1 -1
- package/constructs/App.js +3 -1
- package/node/auth/adapter/link.js +1 -1
- package/node/auth/adapter/oauth.js +1 -1
- package/node/auth/adapter/oidc.js +1 -1
- package/node/future/auth/adapter/adapter.d.ts +6 -1
- package/node/future/auth/adapter/adapter.js +4 -1
- package/node/future/auth/adapter/code.d.ts +1 -4
- package/node/future/auth/adapter/code.js +3 -5
- package/node/future/auth/adapter/facebook.d.ts +6 -1
- package/node/future/auth/adapter/github.d.ts +21 -2
- package/node/future/auth/adapter/google.d.ts +21 -2
- package/node/future/auth/adapter/link.d.ts +1 -4
- package/node/future/auth/adapter/link.js +1 -4
- package/node/future/auth/adapter/oauth.d.ts +9 -1
- package/node/future/auth/adapter/oauth.js +11 -2
- package/node/future/auth/adapter/oidc.js +1 -1
- package/node/future/auth/adapter/spotify.d.ts +6 -1
- package/node/future/auth/handler.d.ts +43 -15
- package/node/future/auth/handler.js +226 -152
- package/node/future/auth/session.d.ts +7 -0
- package/node/future/auth/session.js +1 -0
- package/package.json +1 -1
package/cli/commands/bind.js
CHANGED
package/constructs/App.js
CHANGED
|
@@ -257,7 +257,9 @@ export class App extends CDKApp {
|
|
|
257
257
|
// Tag stacks
|
|
258
258
|
Tags.of(child).add("sst:app", this.name);
|
|
259
259
|
Tags.of(child).add("sst:stage", this.stage);
|
|
260
|
-
if (child instanceof Stack &&
|
|
260
|
+
if (child instanceof Stack &&
|
|
261
|
+
!this.isRunningSSTTest() &&
|
|
262
|
+
this.mode !== "dev") {
|
|
261
263
|
const bootstrap = await useBootstrap();
|
|
262
264
|
const functions = useFunctions();
|
|
263
265
|
const sourcemaps = functions.sourcemaps.forStack(child.stackName);
|
|
@@ -12,7 +12,7 @@ export const LinkAdapter = /* @__PURE__ */ createAdapter((config) => {
|
|
|
12
12
|
const [step] = usePath().slice(-1);
|
|
13
13
|
const callback = "https://" +
|
|
14
14
|
[useDomainName(), ...usePath().slice(0, -1), "callback"].join("/");
|
|
15
|
-
if (step === "authorize") {
|
|
15
|
+
if (step === "authorize" || step === "connect") {
|
|
16
16
|
const url = new URL(callback);
|
|
17
17
|
const claims = useQueryParams();
|
|
18
18
|
url.searchParams.append("token", signer(claims));
|
|
@@ -12,7 +12,7 @@ export const OauthAdapter = /* @__PURE__ */ createAdapter((config) => {
|
|
|
12
12
|
redirect_uris: [callback],
|
|
13
13
|
response_types: ["code"],
|
|
14
14
|
});
|
|
15
|
-
if (step === "authorize") {
|
|
15
|
+
if (step === "authorize" || step === "connect") {
|
|
16
16
|
const code_verifier = generators.codeVerifier();
|
|
17
17
|
const state = generators.state();
|
|
18
18
|
const code_challenge = generators.codeChallenge(code_verifier);
|
|
@@ -11,7 +11,7 @@ export const OidcAdapter = /* @__PURE__ */ createAdapter((config) => {
|
|
|
11
11
|
redirect_uris: [callback],
|
|
12
12
|
response_types: ["id_token"],
|
|
13
13
|
});
|
|
14
|
-
if (step === "authorize") {
|
|
14
|
+
if (step === "authorize" || step === "connect") {
|
|
15
15
|
const nonce = generators.nonce();
|
|
16
16
|
const state = generators.state();
|
|
17
17
|
const url = client.authorizationUrl({
|
|
@@ -7,4 +7,9 @@ export type Adapter<T = any> = (evt: APIGatewayProxyEventV2) => Promise<{
|
|
|
7
7
|
properties: T;
|
|
8
8
|
} | {
|
|
9
9
|
type: "error";
|
|
10
|
-
|
|
10
|
+
error: AdapterError;
|
|
11
|
+
} | undefined>;
|
|
12
|
+
export declare class AdapterError extends Error {
|
|
13
|
+
}
|
|
14
|
+
export declare class AdapterUnknownError extends AdapterError {
|
|
15
|
+
}
|
|
@@ -6,12 +6,9 @@ export declare function CodeAdapter(config: {
|
|
|
6
6
|
}): () => Promise<{
|
|
7
7
|
type: "step";
|
|
8
8
|
properties: APIGatewayProxyStructuredResultV2;
|
|
9
|
-
} | {
|
|
10
|
-
type: "error";
|
|
11
|
-
properties?: undefined;
|
|
12
9
|
} | {
|
|
13
10
|
type: "success";
|
|
14
11
|
properties: {
|
|
15
12
|
claims: any;
|
|
16
13
|
};
|
|
17
|
-
}>;
|
|
14
|
+
} | undefined>;
|
|
@@ -12,7 +12,7 @@ export function CodeAdapter(config) {
|
|
|
12
12
|
}
|
|
13
13
|
return async function () {
|
|
14
14
|
const step = usePathParam("step");
|
|
15
|
-
if (step === "authorize") {
|
|
15
|
+
if (step === "authorize" || step === "connect") {
|
|
16
16
|
const code = generate();
|
|
17
17
|
const claims = useQueryParams();
|
|
18
18
|
delete claims["client_id"];
|
|
@@ -39,7 +39,8 @@ export function CodeAdapter(config) {
|
|
|
39
39
|
const { code, claims } = JSON.parse(decrypt(useCookie("authorization")));
|
|
40
40
|
if (!code || !claims) {
|
|
41
41
|
return {
|
|
42
|
-
type: "
|
|
42
|
+
type: "step",
|
|
43
|
+
properties: await config.onCodeInvalid(code, claims),
|
|
43
44
|
};
|
|
44
45
|
}
|
|
45
46
|
const compare = useQueryParam("code");
|
|
@@ -61,8 +62,5 @@ export function CodeAdapter(config) {
|
|
|
61
62
|
},
|
|
62
63
|
};
|
|
63
64
|
}
|
|
64
|
-
return {
|
|
65
|
-
type: "error",
|
|
66
|
-
};
|
|
67
65
|
};
|
|
68
66
|
}
|
|
@@ -5,7 +5,7 @@ type Config = ({
|
|
|
5
5
|
} & OauthBasicConfig) | ({
|
|
6
6
|
mode: "oidc";
|
|
7
7
|
} & OidcBasicConfig);
|
|
8
|
-
export declare const GithubAdapter: (config: Config) => () => Promise<{
|
|
8
|
+
export declare const GithubAdapter: (config: Config) => (() => Promise<{
|
|
9
9
|
type: "success";
|
|
10
10
|
properties: {
|
|
11
11
|
tokenset: import("openid-client").TokenSet;
|
|
@@ -19,5 +19,24 @@ export declare const GithubAdapter: (config: Config) => () => Promise<{
|
|
|
19
19
|
location: string;
|
|
20
20
|
};
|
|
21
21
|
};
|
|
22
|
-
}
|
|
22
|
+
}>) | (() => Promise<{
|
|
23
|
+
type: "success";
|
|
24
|
+
properties: {
|
|
25
|
+
tokenset: import("openid-client").TokenSet;
|
|
26
|
+
client: import("openid-client").BaseClient;
|
|
27
|
+
};
|
|
28
|
+
} | {
|
|
29
|
+
type: "step";
|
|
30
|
+
properties: {
|
|
31
|
+
statusCode: number;
|
|
32
|
+
headers: {
|
|
33
|
+
location: string;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
error?: undefined;
|
|
37
|
+
} | {
|
|
38
|
+
type: "error";
|
|
39
|
+
error: import("./oauth.js").OauthError;
|
|
40
|
+
properties?: undefined;
|
|
41
|
+
} | undefined>);
|
|
23
42
|
export {};
|
|
@@ -10,7 +10,7 @@ type GoogleConfig = (OauthBasicConfig & {
|
|
|
10
10
|
mode: "oidc";
|
|
11
11
|
prompt?: GooglePrompt;
|
|
12
12
|
});
|
|
13
|
-
export declare function GoogleAdapter(config: GoogleConfig): () => Promise<{
|
|
13
|
+
export declare function GoogleAdapter(config: GoogleConfig): (() => Promise<{
|
|
14
14
|
type: "success";
|
|
15
15
|
properties: {
|
|
16
16
|
tokenset: import("openid-client").TokenSet;
|
|
@@ -24,5 +24,24 @@ export declare function GoogleAdapter(config: GoogleConfig): () => Promise<{
|
|
|
24
24
|
location: string;
|
|
25
25
|
};
|
|
26
26
|
};
|
|
27
|
-
}
|
|
27
|
+
}>) | (() => Promise<{
|
|
28
|
+
type: "success";
|
|
29
|
+
properties: {
|
|
30
|
+
tokenset: import("openid-client").TokenSet;
|
|
31
|
+
client: import("openid-client").BaseClient;
|
|
32
|
+
};
|
|
33
|
+
} | {
|
|
34
|
+
type: "step";
|
|
35
|
+
properties: {
|
|
36
|
+
statusCode: number;
|
|
37
|
+
headers: {
|
|
38
|
+
location: string;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
error?: undefined;
|
|
42
|
+
} | {
|
|
43
|
+
type: "error";
|
|
44
|
+
error: import("./oauth.js").OauthError;
|
|
45
|
+
properties?: undefined;
|
|
46
|
+
} | undefined>);
|
|
28
47
|
export {};
|
|
@@ -14,7 +14,7 @@ export function LinkAdapter(config) {
|
|
|
14
14
|
});
|
|
15
15
|
const callback = "https://" + useDomainName() + "/callback";
|
|
16
16
|
const step = usePathParam("step");
|
|
17
|
-
if (step === "authorize") {
|
|
17
|
+
if (step === "authorize" || step === "connect") {
|
|
18
18
|
const url = new URL(callback);
|
|
19
19
|
const claims = useQueryParams();
|
|
20
20
|
url.searchParams.append("token", signer(claims));
|
|
@@ -40,8 +40,5 @@ export function LinkAdapter(config) {
|
|
|
40
40
|
}
|
|
41
41
|
catch (ex) { }
|
|
42
42
|
}
|
|
43
|
-
return {
|
|
44
|
-
type: "error",
|
|
45
|
-
};
|
|
46
43
|
};
|
|
47
44
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BaseClient, Issuer, TokenSet } from "openid-client";
|
|
2
|
+
import { AdapterError } from "./adapter.js";
|
|
2
3
|
export interface OauthBasicConfig {
|
|
3
4
|
/**
|
|
4
5
|
* The clientID provided by the third party oauth service
|
|
@@ -24,6 +25,8 @@ export interface OauthBasicConfig {
|
|
|
24
25
|
export interface OauthConfig extends OauthBasicConfig {
|
|
25
26
|
issuer: Issuer;
|
|
26
27
|
}
|
|
28
|
+
export declare class OauthError extends AdapterError {
|
|
29
|
+
}
|
|
27
30
|
export declare const OauthAdapter: (config: OauthConfig) => () => Promise<{
|
|
28
31
|
type: "success";
|
|
29
32
|
properties: {
|
|
@@ -38,4 +41,9 @@ export declare const OauthAdapter: (config: OauthConfig) => () => Promise<{
|
|
|
38
41
|
location: string;
|
|
39
42
|
};
|
|
40
43
|
};
|
|
41
|
-
|
|
44
|
+
error?: undefined;
|
|
45
|
+
} | {
|
|
46
|
+
type: "error";
|
|
47
|
+
error: OauthError;
|
|
48
|
+
properties?: undefined;
|
|
49
|
+
} | undefined>;
|
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
import { generators } from "openid-client";
|
|
2
2
|
import { useCookie, useDomainName, usePathParam, useQueryParams, useResponse, } from "../../../api/index.js";
|
|
3
|
+
import { AdapterError } from "./adapter.js";
|
|
4
|
+
export class OauthError extends AdapterError {
|
|
5
|
+
}
|
|
3
6
|
export const OauthAdapter =
|
|
4
7
|
/* @__PURE__ */
|
|
5
8
|
(config) => {
|
|
6
9
|
return async function () {
|
|
7
10
|
const step = usePathParam("step");
|
|
8
11
|
const callback = "https://" + useDomainName() + "/callback";
|
|
12
|
+
console.log("callback", callback);
|
|
9
13
|
const client = new config.issuer.Client({
|
|
10
14
|
client_id: config.clientID,
|
|
11
15
|
client_secret: config.clientSecret,
|
|
12
16
|
redirect_uris: [callback],
|
|
13
17
|
response_types: ["code"],
|
|
14
18
|
});
|
|
15
|
-
if (step === "authorize") {
|
|
19
|
+
if (step === "authorize" || step === "connect") {
|
|
16
20
|
const code_verifier = generators.codeVerifier();
|
|
17
21
|
const state = generators.state();
|
|
18
22
|
const code_challenge = generators.codeChallenge(code_verifier);
|
|
@@ -45,6 +49,12 @@ export const OauthAdapter =
|
|
|
45
49
|
}
|
|
46
50
|
if (step === "callback") {
|
|
47
51
|
const params = useQueryParams();
|
|
52
|
+
if (params.error) {
|
|
53
|
+
return {
|
|
54
|
+
type: "error",
|
|
55
|
+
error: new OauthError(params.error),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
48
58
|
const code_verifier = useCookie("auth_code_verifier");
|
|
49
59
|
const state = useCookie("auth_state");
|
|
50
60
|
const tokenset = await client[config.issuer.metadata.userinfo_endpoint
|
|
@@ -62,6 +72,5 @@ export const OauthAdapter =
|
|
|
62
72
|
};
|
|
63
73
|
return x;
|
|
64
74
|
}
|
|
65
|
-
throw new Error("Invalid auth request");
|
|
66
75
|
};
|
|
67
76
|
};
|
|
@@ -9,7 +9,7 @@ export const OidcAdapter = /* @__PURE__ */ (config) => {
|
|
|
9
9
|
redirect_uris: [callback],
|
|
10
10
|
response_types: ["id_token"],
|
|
11
11
|
});
|
|
12
|
-
if (step === "authorize") {
|
|
12
|
+
if (step === "authorize" || step === "connect") {
|
|
13
13
|
const nonce = generators.nonce();
|
|
14
14
|
const state = generators.state();
|
|
15
15
|
const url = client.authorizationUrl({
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { APIGatewayProxyEventV2, APIGatewayProxyStructuredResultV2 } from "aws-lambda";
|
|
2
2
|
import { Adapter } from "./adapter/adapter.js";
|
|
3
3
|
import { SignerOptions } from "fast-jwt";
|
|
4
|
-
import { SessionBuilder
|
|
5
|
-
interface OnSuccessResponder<T
|
|
4
|
+
import { SessionBuilder } from "./session.js";
|
|
5
|
+
interface OnSuccessResponder<T extends {
|
|
6
|
+
type: any;
|
|
7
|
+
properties: any;
|
|
8
|
+
}> {
|
|
6
9
|
session(input: T & Partial<SignerOptions>): {
|
|
7
10
|
type: "session";
|
|
8
11
|
properties: T;
|
|
@@ -12,10 +15,25 @@ interface OnSuccessResponder<T> {
|
|
|
12
15
|
properties: typeof input;
|
|
13
16
|
};
|
|
14
17
|
}
|
|
15
|
-
export declare class UnknownProviderError {
|
|
18
|
+
export declare class UnknownProviderError extends Error {
|
|
16
19
|
provider?: string | undefined;
|
|
17
20
|
constructor(provider?: string | undefined);
|
|
18
21
|
}
|
|
22
|
+
export declare class MissingParameterError extends Error {
|
|
23
|
+
parameter: string;
|
|
24
|
+
constructor(parameter: string);
|
|
25
|
+
}
|
|
26
|
+
export declare class UnknownStateError extends Error {
|
|
27
|
+
constructor();
|
|
28
|
+
}
|
|
29
|
+
export declare class UnauthorizedClientError extends Error {
|
|
30
|
+
client: string;
|
|
31
|
+
redirect_uri: string;
|
|
32
|
+
constructor(client: string, redirect_uri: string);
|
|
33
|
+
}
|
|
34
|
+
export declare class InvalidSessionError extends Error {
|
|
35
|
+
constructor();
|
|
36
|
+
}
|
|
19
37
|
export declare function AuthHandler<Providers extends Record<string, Adapter<any>>, Sessions extends SessionBuilder, Result = {
|
|
20
38
|
[key in keyof Providers]: {
|
|
21
39
|
provider: key;
|
|
@@ -25,22 +43,32 @@ export declare function AuthHandler<Providers extends Record<string, Adapter<any
|
|
|
25
43
|
}[keyof Providers]>(input: {
|
|
26
44
|
providers: Providers;
|
|
27
45
|
sessions?: Sessions;
|
|
28
|
-
/** @deprecated use allowClient callback instead */
|
|
46
|
+
/** @deprecated use callbacks.auth.allowClient callback instead */
|
|
29
47
|
clients?: () => Promise<Record<string, string>>;
|
|
48
|
+
/** @deprecated use callbacks.auth.allowClient callback instead */
|
|
30
49
|
allowClient?: (clientID: string, redirect: string) => Promise<boolean>;
|
|
50
|
+
/** @deprecated use callbacks.auth.start callback instead */
|
|
31
51
|
onAuthorize?: (event: APIGatewayProxyEventV2) => Promise<void | keyof Providers>;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
52
|
+
/** @deprecated use callbacks.auth.success callback instead */
|
|
53
|
+
onSuccess?: (input: Result, response: OnSuccessResponder<Sessions["$typeValues"]>) => Promise<ReturnType<OnSuccessResponder<Sessions["$typeValues"]>[keyof OnSuccessResponder<any>]>>;
|
|
54
|
+
/** @deprecated */
|
|
55
|
+
onIndex?: (event: APIGatewayProxyEventV2) => Promise<APIGatewayProxyStructuredResultV2>;
|
|
56
|
+
/** @deprecated use on.error callback instead */
|
|
57
|
+
onError?: (error: MissingParameterError | UnauthorizedClientError | UnknownProviderError) => Promise<APIGatewayProxyStructuredResultV2 | undefined>;
|
|
58
|
+
callbacks: {
|
|
59
|
+
index?(event: APIGatewayProxyEventV2): Promise<APIGatewayProxyStructuredResultV2>;
|
|
60
|
+
error?(error: UnknownStateError): Promise<APIGatewayProxyStructuredResultV2 | undefined>;
|
|
61
|
+
auth: {
|
|
62
|
+
error?(error: MissingParameterError | UnauthorizedClientError | UnknownProviderError): Promise<APIGatewayProxyStructuredResultV2 | undefined>;
|
|
63
|
+
start?(event: APIGatewayProxyEventV2): Promise<void>;
|
|
64
|
+
allowClient(clientID: string, redirect: string): Promise<boolean>;
|
|
65
|
+
success(input: Result, response: OnSuccessResponder<Sessions["$typeValues"]>): Promise<ReturnType<OnSuccessResponder<Sessions["$typeValues"]>[keyof OnSuccessResponder<any>]>>;
|
|
36
66
|
};
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
67
|
+
connect?: {
|
|
68
|
+
error?(error: InvalidSessionError | UnknownProviderError): Promise<APIGatewayProxyStructuredResultV2 | undefined>;
|
|
69
|
+
start?(session: Sessions["$typeValues"], event: APIGatewayProxyEventV2): Promise<void>;
|
|
70
|
+
success?(session: Sessions["$typeValues"], input: Result): Promise<APIGatewayProxyStructuredResultV2>;
|
|
41
71
|
};
|
|
42
|
-
}
|
|
43
|
-
onIndex?: (event: APIGatewayProxyEventV2) => Promise<APIGatewayProxyStructuredResultV2>;
|
|
44
|
-
onError?: (error: UnknownProviderError) => Promise<APIGatewayProxyStructuredResultV2 | undefined>;
|
|
72
|
+
};
|
|
45
73
|
}): (event: APIGatewayProxyEventV2, context: import("aws-lambda").Context) => Promise<APIGatewayProxyStructuredResultV2>;
|
|
46
74
|
export {};
|
|
@@ -1,47 +1,61 @@
|
|
|
1
1
|
import { createSigner, createVerifier } from "fast-jwt";
|
|
2
2
|
import { ApiHandler, useCookie, useCookies, useFormValue, usePathParam, useQueryParam, useQueryParams, useResponse, } from "../../api/index.js";
|
|
3
3
|
import { Config } from "../../config/index.js";
|
|
4
|
-
export class UnknownProviderError {
|
|
4
|
+
export class UnknownProviderError extends Error {
|
|
5
5
|
provider;
|
|
6
6
|
constructor(provider) {
|
|
7
|
+
super("Unknown provider: " + provider);
|
|
7
8
|
this.provider = provider;
|
|
8
9
|
}
|
|
9
10
|
}
|
|
11
|
+
export class MissingParameterError extends Error {
|
|
12
|
+
parameter;
|
|
13
|
+
constructor(parameter) {
|
|
14
|
+
super("Missing parameter: " + parameter);
|
|
15
|
+
this.parameter = parameter;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export class UnknownStateError extends Error {
|
|
19
|
+
constructor() {
|
|
20
|
+
super("The user's browser was in an unknown state. This could be because certain cookies expired or the user switched browsers in the middle of an authentication flow");
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export class UnauthorizedClientError extends Error {
|
|
24
|
+
client;
|
|
25
|
+
redirect_uri;
|
|
26
|
+
constructor(client, redirect_uri) {
|
|
27
|
+
super("Unauthorized client");
|
|
28
|
+
this.client = client;
|
|
29
|
+
this.redirect_uri = redirect_uri;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export class InvalidSessionError extends Error {
|
|
33
|
+
constructor() {
|
|
34
|
+
super("Invalid session");
|
|
35
|
+
}
|
|
36
|
+
}
|
|
10
37
|
export function AuthHandler(input) {
|
|
38
|
+
// Remap deprecrated stuff
|
|
39
|
+
const { allowClient, clients, onError, onSuccess, onAuthorize, onIndex } = input;
|
|
40
|
+
if (onError && !input.callbacks.auth.error)
|
|
41
|
+
input.callbacks.auth.error = onError;
|
|
42
|
+
if (onSuccess && !input.callbacks.auth.success)
|
|
43
|
+
input.callbacks.auth.success = onSuccess;
|
|
44
|
+
if (onIndex && !input.callbacks.index)
|
|
45
|
+
input.callbacks.index = onIndex;
|
|
46
|
+
if (onAuthorize && !input.callbacks.auth.start)
|
|
47
|
+
input.callbacks.auth.start = async (evt) => {
|
|
48
|
+
await onAuthorize(evt);
|
|
49
|
+
};
|
|
50
|
+
if (allowClient && !input.callbacks.auth.allowClient)
|
|
51
|
+
input.callbacks.auth.allowClient = allowClient;
|
|
52
|
+
if (clients && !input.callbacks.auth.allowClient)
|
|
53
|
+
input.callbacks.auth.allowClient = async (clientID, redirect) => {
|
|
54
|
+
const list = await clients();
|
|
55
|
+
return list[clientID].startsWith(redirect);
|
|
56
|
+
};
|
|
11
57
|
return ApiHandler(async (evt) => {
|
|
12
58
|
const step = usePathParam("step");
|
|
13
|
-
if (!step) {
|
|
14
|
-
if (input.onIndex) {
|
|
15
|
-
return input.onIndex(evt);
|
|
16
|
-
}
|
|
17
|
-
const clients = (await input.clients?.()) || {};
|
|
18
|
-
return {
|
|
19
|
-
statusCode: 200,
|
|
20
|
-
headers: {
|
|
21
|
-
"Content-Type": "text/html",
|
|
22
|
-
},
|
|
23
|
-
body: `
|
|
24
|
-
<html>
|
|
25
|
-
<head>
|
|
26
|
-
<link rel="icon" href="data:,">
|
|
27
|
-
</head>
|
|
28
|
-
<body>
|
|
29
|
-
<table>
|
|
30
|
-
<tr>${Object.keys(clients).map((client) => `<td>${client}</td>`)}</tr>
|
|
31
|
-
${Object.keys(input.providers).map((name) => {
|
|
32
|
-
return `<tr>
|
|
33
|
-
${Object.keys(clients).map((client_id) => {
|
|
34
|
-
const redirect_uri = clients[client_id];
|
|
35
|
-
return `<td><a href="/authorize?provider=${name}&response_type=token&client_id=${client_id}&redirect_uri=${redirect_uri}">${name} - ${client_id}</a></td>`;
|
|
36
|
-
})}
|
|
37
|
-
</tr>`;
|
|
38
|
-
})}
|
|
39
|
-
</table>
|
|
40
|
-
</body>
|
|
41
|
-
</html>
|
|
42
|
-
`,
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
59
|
if (step === "favicon.ico") {
|
|
46
60
|
return {
|
|
47
61
|
statusCode: 404,
|
|
@@ -90,62 +104,75 @@ export function AuthHandler(input) {
|
|
|
90
104
|
};
|
|
91
105
|
}
|
|
92
106
|
let provider = useCookie("provider");
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
provider = result;
|
|
99
|
-
}
|
|
107
|
+
let response_type = useCookie("response_type");
|
|
108
|
+
let redirect_uri = useCookie("redirect_uri");
|
|
109
|
+
console.log("step", step);
|
|
110
|
+
if (step === "connect") {
|
|
111
|
+
provider = useFormValue("provider") || undefined;
|
|
100
112
|
if (!provider) {
|
|
101
113
|
return {
|
|
102
114
|
statusCode: 400,
|
|
103
115
|
body: "Missing provider",
|
|
104
116
|
};
|
|
105
117
|
}
|
|
106
|
-
const
|
|
118
|
+
const token = useFormValue("token");
|
|
119
|
+
const verified = input.sessions?.verify(token);
|
|
120
|
+
if (!verified) {
|
|
121
|
+
return ((await input.callbacks.connect?.error?.(new InvalidSessionError())) || {
|
|
122
|
+
statusCode: 401,
|
|
123
|
+
body: "Invalid session",
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
await input.callbacks.connect?.start?.(verified, evt);
|
|
127
|
+
response_type = "connect";
|
|
128
|
+
useResponse().cookies({
|
|
129
|
+
provider,
|
|
130
|
+
response_type: "connect",
|
|
131
|
+
sst_auth_token: token,
|
|
132
|
+
}, {
|
|
133
|
+
maxAge: 60 * 15,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
if (step === "authorize") {
|
|
137
|
+
provider = useQueryParam("provider");
|
|
138
|
+
response_type = useQueryParam("response_type") || response_type;
|
|
139
|
+
redirect_uri = useQueryParam("redirect_uri") || redirect_uri;
|
|
140
|
+
const { client_id, state } = {
|
|
107
141
|
...useCookies(),
|
|
108
142
|
...useQueryParams(),
|
|
109
143
|
};
|
|
110
|
-
if (!redirect_uri) {
|
|
111
|
-
return {
|
|
112
|
-
statusCode: 400,
|
|
113
|
-
body: "Missing redirect_uri",
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
144
|
if (!provider) {
|
|
117
|
-
return {
|
|
145
|
+
return ((await input.callbacks.auth.error?.(new MissingParameterError("provider"))) || {
|
|
118
146
|
statusCode: 400,
|
|
119
147
|
body: "Missing provider",
|
|
120
|
-
};
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
if (!redirect_uri) {
|
|
151
|
+
return ((await input.callbacks.auth.error?.(new MissingParameterError("redirect_uri"))) || {
|
|
152
|
+
statusCode: 400,
|
|
153
|
+
body: "Missing redirect_uri",
|
|
154
|
+
});
|
|
121
155
|
}
|
|
122
156
|
if (!response_type) {
|
|
123
|
-
return {
|
|
157
|
+
return ((await input.callbacks.auth.error?.(new MissingParameterError("response_type"))) || {
|
|
124
158
|
statusCode: 400,
|
|
125
159
|
body: "Missing response_type",
|
|
126
|
-
};
|
|
160
|
+
});
|
|
127
161
|
}
|
|
128
162
|
if (!client_id) {
|
|
129
|
-
return {
|
|
163
|
+
return ((await input.callbacks.auth.error?.(new MissingParameterError("client_id"))) || {
|
|
130
164
|
statusCode: 400,
|
|
131
165
|
body: "Missing client_id",
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
if (input.clients) {
|
|
135
|
-
const clients = await input.clients();
|
|
136
|
-
if (clients[client_id] !== redirect_uri) {
|
|
137
|
-
return {
|
|
138
|
-
statusCode: 400,
|
|
139
|
-
body: "Invalid redirect_uri",
|
|
140
|
-
};
|
|
141
|
-
}
|
|
166
|
+
});
|
|
142
167
|
}
|
|
143
|
-
if (input.allowClient
|
|
144
|
-
|
|
145
|
-
return {
|
|
168
|
+
if (!(await input.callbacks.auth.allowClient(client_id, redirect_uri))) {
|
|
169
|
+
return ((await input.callbacks.auth.error?.(new UnauthorizedClientError(client_id, redirect_uri))) || {
|
|
146
170
|
statusCode: 400,
|
|
147
171
|
body: "Invalid redirect_uri",
|
|
148
|
-
};
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
if (input.callbacks.auth.start) {
|
|
175
|
+
await input.callbacks.auth.start(evt);
|
|
149
176
|
}
|
|
150
177
|
useResponse().cookies({
|
|
151
178
|
provider: provider,
|
|
@@ -160,119 +187,166 @@ export function AuthHandler(input) {
|
|
|
160
187
|
httpOnly: true,
|
|
161
188
|
});
|
|
162
189
|
}
|
|
190
|
+
if (!response_type) {
|
|
191
|
+
return ((await input.callbacks.error?.(new UnknownStateError())) || {
|
|
192
|
+
statusCode: 400,
|
|
193
|
+
body: new UnknownStateError().message,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
163
196
|
if (!provider || !input.providers[provider]) {
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
197
|
+
const err = new UnknownProviderError(provider);
|
|
198
|
+
return ((response_type === "connect"
|
|
199
|
+
? input.callbacks.connect?.error
|
|
200
|
+
: input.callbacks.auth.error)?.(err) || {
|
|
168
201
|
statusCode: 400,
|
|
169
|
-
body:
|
|
202
|
+
body: err.toString(),
|
|
170
203
|
headers: {
|
|
171
204
|
"Content-Type": "text/html",
|
|
172
205
|
},
|
|
173
|
-
};
|
|
206
|
+
});
|
|
174
207
|
}
|
|
175
208
|
const adapter = input.providers[provider];
|
|
176
209
|
const result = await adapter(evt);
|
|
210
|
+
if (!result) {
|
|
211
|
+
return {
|
|
212
|
+
statusCode: 404,
|
|
213
|
+
body: "Not found",
|
|
214
|
+
};
|
|
215
|
+
}
|
|
177
216
|
if (result.type === "step") {
|
|
178
217
|
return result.properties;
|
|
179
218
|
}
|
|
180
219
|
if (result.type === "success") {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
},
|
|
191
|
-
session(input) {
|
|
192
|
-
return {
|
|
193
|
-
type: "session",
|
|
194
|
-
properties: input,
|
|
195
|
-
};
|
|
196
|
-
},
|
|
197
|
-
});
|
|
198
|
-
if (onSuccess.type === "session") {
|
|
199
|
-
const { type, properties, ...rest } = onSuccess.properties;
|
|
200
|
-
// @ts-expect-error
|
|
201
|
-
const priv = Config[process.env.AUTH_ID + "PrivateKey"];
|
|
202
|
-
const signer = createSigner({
|
|
203
|
-
...rest,
|
|
204
|
-
key: priv,
|
|
205
|
-
algorithm: "RS512",
|
|
206
|
-
});
|
|
207
|
-
const token = signer({
|
|
208
|
-
type,
|
|
209
|
-
properties,
|
|
210
|
-
});
|
|
211
|
-
useResponse()
|
|
212
|
-
.cookie({
|
|
213
|
-
key: "sst_auth_token",
|
|
214
|
-
value: token,
|
|
215
|
-
maxAge: 10 * 365 * 24 * 60 * 60,
|
|
216
|
-
})
|
|
217
|
-
.cookies({
|
|
220
|
+
if (response_type === "connect") {
|
|
221
|
+
const session = input.sessions?.use();
|
|
222
|
+
if (!session) {
|
|
223
|
+
return ((await input.callbacks.connect?.error?.(new InvalidSessionError())) || {
|
|
224
|
+
statusCode: 401,
|
|
225
|
+
body: "Invalid session",
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
useResponse().cookies({
|
|
218
229
|
provider: "",
|
|
219
230
|
response_type: "",
|
|
220
|
-
|
|
221
|
-
redirect_uri: "",
|
|
222
|
-
state: "",
|
|
231
|
+
sst_auth_token: "",
|
|
223
232
|
}, {
|
|
224
233
|
expires: new Date(1),
|
|
225
234
|
});
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
...
|
|
229
|
-
};
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
235
|
+
return input.callbacks.connect?.success(session, {
|
|
236
|
+
provider,
|
|
237
|
+
...result.properties,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
if (response_type === "token" || response_type === "code") {
|
|
241
|
+
const onSuccess = await input.callbacks.auth.success({
|
|
242
|
+
provider,
|
|
243
|
+
...result.properties,
|
|
244
|
+
}, {
|
|
245
|
+
http(input) {
|
|
246
|
+
return {
|
|
247
|
+
type: "http",
|
|
248
|
+
properties: input,
|
|
249
|
+
};
|
|
250
|
+
},
|
|
251
|
+
session(input) {
|
|
252
|
+
return {
|
|
253
|
+
type: "session",
|
|
254
|
+
properties: input,
|
|
255
|
+
};
|
|
256
|
+
},
|
|
257
|
+
});
|
|
258
|
+
if (onSuccess.type === "session") {
|
|
259
|
+
const { type, properties, ...rest } = onSuccess.properties;
|
|
260
|
+
// @ts-expect-error
|
|
261
|
+
const priv = Config[process.env.AUTH_ID + "PrivateKey"];
|
|
262
|
+
const signer = createSigner({
|
|
263
|
+
...rest,
|
|
246
264
|
key: priv,
|
|
247
265
|
algorithm: "RS512",
|
|
248
|
-
})({
|
|
249
|
-
client_id,
|
|
250
|
-
redirect_uri,
|
|
251
|
-
token: token,
|
|
252
266
|
});
|
|
253
|
-
const
|
|
254
|
-
|
|
255
|
-
|
|
267
|
+
const token = signer({
|
|
268
|
+
type,
|
|
269
|
+
properties,
|
|
270
|
+
});
|
|
271
|
+
useResponse()
|
|
272
|
+
.cookie({
|
|
273
|
+
key: "sst_auth_token",
|
|
274
|
+
value: token,
|
|
275
|
+
maxAge: 10 * 365 * 24 * 60 * 60,
|
|
276
|
+
})
|
|
277
|
+
.cookies({
|
|
278
|
+
provider: "",
|
|
279
|
+
response_type: "",
|
|
280
|
+
client_id: "",
|
|
281
|
+
redirect_uri: "",
|
|
282
|
+
state: "",
|
|
283
|
+
}, {
|
|
284
|
+
expires: new Date(1),
|
|
285
|
+
});
|
|
286
|
+
const { client_id, redirect_uri, state } = useCookies();
|
|
287
|
+
if (response_type === "token") {
|
|
288
|
+
const location = new URL(redirect_uri);
|
|
289
|
+
location.hash = `access_token=${token}&state=${state || ""}`;
|
|
290
|
+
return {
|
|
291
|
+
statusCode: 302,
|
|
292
|
+
headers: {
|
|
293
|
+
Location: location.href,
|
|
294
|
+
},
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
if (response_type === "code") {
|
|
298
|
+
// This allows the code to be reused within a 30 second window
|
|
299
|
+
// The code should be single use but we're making this tradeoff to remain stateless
|
|
300
|
+
// In the future can store this in a dynamo table to ensure single use
|
|
301
|
+
const code = createSigner({
|
|
302
|
+
expiresIn: 1000 * 60 * 5,
|
|
303
|
+
key: priv,
|
|
304
|
+
algorithm: "RS512",
|
|
305
|
+
})({
|
|
306
|
+
client_id,
|
|
307
|
+
redirect_uri,
|
|
308
|
+
token: token,
|
|
309
|
+
});
|
|
310
|
+
const location = new URL(redirect_uri);
|
|
311
|
+
location.searchParams.set("code", code);
|
|
312
|
+
location.searchParams.set("state", state || "");
|
|
313
|
+
return {
|
|
314
|
+
statusCode: 302,
|
|
315
|
+
headers: {
|
|
316
|
+
Location: location.href,
|
|
317
|
+
},
|
|
318
|
+
};
|
|
319
|
+
}
|
|
256
320
|
return {
|
|
257
|
-
statusCode:
|
|
258
|
-
|
|
259
|
-
Location: location.href,
|
|
260
|
-
},
|
|
321
|
+
statusCode: 400,
|
|
322
|
+
body: `Unsupported response_type: ${response_type}`,
|
|
261
323
|
};
|
|
262
324
|
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
};
|
|
267
|
-
}
|
|
268
|
-
if (onSuccess.type === "http") {
|
|
269
|
-
return onSuccess.properties;
|
|
325
|
+
if (onSuccess.type === "http") {
|
|
326
|
+
return onSuccess.properties;
|
|
327
|
+
}
|
|
270
328
|
}
|
|
271
329
|
}
|
|
272
330
|
if (result.type === "error") {
|
|
331
|
+
if (response_type === "connect") {
|
|
332
|
+
return ((await input.callbacks.connect?.error?.(result.error)) || {
|
|
333
|
+
statusCode: 400,
|
|
334
|
+
body: result.error.message,
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
if (!redirect_uri) {
|
|
338
|
+
return ((await input.callbacks.auth.error?.(new UnknownStateError())) || {
|
|
339
|
+
statusCode: 400,
|
|
340
|
+
body: new UnknownStateError().message,
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
const location = new URL(redirect_uri);
|
|
344
|
+
location.searchParams.set("error", result.error.message);
|
|
273
345
|
return {
|
|
274
|
-
statusCode:
|
|
275
|
-
|
|
346
|
+
statusCode: 302,
|
|
347
|
+
headers: {
|
|
348
|
+
Location: location.toString(),
|
|
349
|
+
},
|
|
276
350
|
};
|
|
277
351
|
}
|
|
278
352
|
});
|
|
@@ -67,5 +67,12 @@ export declare function createSessionBuilder<SessionTypes extends Record<string,
|
|
|
67
67
|
properties: {};
|
|
68
68
|
};
|
|
69
69
|
$type: SessionTypes;
|
|
70
|
+
$typeValues: { [type in keyof SessionTypes]: {
|
|
71
|
+
type: type;
|
|
72
|
+
properties: SessionTypes[type];
|
|
73
|
+
}; }[keyof SessionTypes] | {
|
|
74
|
+
type: "public";
|
|
75
|
+
properties: {};
|
|
76
|
+
};
|
|
70
77
|
};
|
|
71
78
|
export {};
|