@sst-community/sst 0.0.0-placeholder.0 → 4.17.3
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 +84 -0
- package/bin/sst.mjs +33 -0
- package/dist/auth/adapter/adapter.d.ts +23 -0
- package/dist/auth/adapter/adapter.js +4 -0
- package/dist/auth/adapter/apple.d.ts +5 -0
- package/dist/auth/adapter/apple.js +22 -0
- package/dist/auth/adapter/code.d.ts +7 -0
- package/dist/auth/adapter/code.js +47 -0
- package/dist/auth/adapter/facebook.d.ts +5 -0
- package/dist/auth/adapter/facebook.js +27 -0
- package/dist/auth/adapter/github.d.ts +7 -0
- package/dist/auth/adapter/github.js +15 -0
- package/dist/auth/adapter/google.d.ts +17 -0
- package/dist/auth/adapter/google.js +22 -0
- package/dist/auth/adapter/index.d.ts +11 -0
- package/dist/auth/adapter/index.js +10 -0
- package/dist/auth/adapter/link.d.ts +5 -0
- package/dist/auth/adapter/link.js +27 -0
- package/dist/auth/adapter/microsoft.d.ts +11 -0
- package/dist/auth/adapter/microsoft.js +16 -0
- package/dist/auth/adapter/oauth.d.ts +33 -0
- package/dist/auth/adapter/oauth.js +82 -0
- package/dist/auth/adapter/oidc.d.ts +19 -0
- package/dist/auth/adapter/oidc.js +48 -0
- package/dist/auth/adapter/spotify.d.ts +12 -0
- package/dist/auth/adapter/spotify.js +22 -0
- package/dist/auth/example/bun.d.ts +2 -0
- package/dist/auth/example/bun.js +46 -0
- package/dist/auth/handler.d.ts +57 -0
- package/dist/auth/handler.js +206 -0
- package/dist/auth/index.d.ts +10 -0
- package/dist/auth/index.js +10 -0
- package/dist/auth/session.d.ts +25 -0
- package/dist/auth/session.js +28 -0
- package/dist/aws/auth.d.ts +9 -0
- package/dist/aws/auth.js +12 -0
- package/dist/aws/bus.d.ts +28 -0
- package/dist/aws/bus.js +66 -0
- package/dist/aws/client.d.ts +8 -0
- package/dist/aws/client.js +54 -0
- package/dist/aws/realtime.d.ts +118 -0
- package/dist/aws/realtime.js +83 -0
- package/dist/aws/task.d.ts +228 -0
- package/dist/aws/task.js +232 -0
- package/dist/aws/workflow.d.ts +324 -0
- package/dist/aws/workflow.js +504 -0
- package/dist/event/index.d.ts +72 -0
- package/dist/event/index.js +35 -0
- package/dist/event/validator.d.ts +4 -0
- package/dist/event/validator.js +11 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +17 -0
- package/dist/realtime/index.d.ts +25 -0
- package/dist/realtime/index.js +24 -0
- package/dist/resource/cloudflare.d.ts +5 -0
- package/dist/resource/cloudflare.js +30 -0
- package/dist/resource/index.d.ts +1 -0
- package/dist/resource/index.js +1 -0
- package/dist/resource/node.d.ts +4 -0
- package/dist/resource/node.js +38 -0
- package/dist/resource/shared.d.ts +9 -0
- package/dist/resource/shared.js +62 -0
- package/dist/resource.d.ts +1 -0
- package/dist/resource.js +1 -0
- package/dist/util/prettify.d.ts +3 -0
- package/dist/util/prettify.js +1 -0
- package/dist/vector/index.d.ts +247 -0
- package/dist/vector/index.js +107 -0
- package/package.json +73 -1
package/README.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
<h1 align="center">sst-community</h1>
|
|
2
|
+
|
|
3
|
+
<p align="center">A community-maintained fork of <a href="https://github.com/anomalyco/sst">SST</a></p>
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Build full-stack apps on your own infrastructure.
|
|
8
|
+
|
|
9
|
+
> [!NOTE]
|
|
10
|
+
> **sst-community** is a community-maintained fork of [SST](https://github.com/anomalyco/sst). It is not affiliated with SST or Anomaly. It tracks upstream releases and carries fixes that haven't landed upstream yet.
|
|
11
|
+
>
|
|
12
|
+
> Releases have their own version numbers, because the fork moves faster than SST: up to 4.17.2 they followed SST's, and from there on the fork's 4.18.0 isn't SST's 4.18.0. Each release's notes say which SST release it's built on and list what it changes, and `sst version` shows the SST release too. Telemetry is off. Docs: [sst-community.github.io/sst](https://sst-community.github.io/sst/docs/). Chat: [Discord](https://discord.gg/DQWT3WGVm2).
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
For JavaScript projects, install the fork locally under the name `sst`, so the CLI version is tracked with your app and `import ... from "sst"` keeps working. You can then run the CLI with the same package manager.
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install sst@npm:@sst-community/sst
|
|
20
|
+
# pnpm add sst@npm:@sst-community/sst
|
|
21
|
+
# bun add sst@npm:@sst-community/sst
|
|
22
|
+
# yarn add sst@npm:@sst-community/sst
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
To switch an existing project from SST, change its dependency to `"sst": "npm:@sst-community/sst@<version>"` and reinstall. `sst upgrade` keeps it pointing at the fork.
|
|
26
|
+
|
|
27
|
+
If you are not using JavaScript, you can install the CLI globally.
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
curl -fsSL https://raw.githubusercontent.com/sst-community/sst/main/install | bash
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
To install a specific version.
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
curl -fsSL https://raw.githubusercontent.com/sst-community/sst/main/install | VERSION=4.17.2 bash
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
#### Manually
|
|
40
|
+
|
|
41
|
+
Download the pre-compiled binaries from the [releases](https://github.com/sst-community/sst/releases/latest) page and copy to the desired location. On Linux, the `.deb` and `.rpm` packages there install with `sudo dpkg -i` and `sudo rpm -i`.
|
|
42
|
+
|
|
43
|
+
## Get Started
|
|
44
|
+
|
|
45
|
+
Get started with your favorite framework:
|
|
46
|
+
|
|
47
|
+
- [Next.js](https://sst-community.github.io/sst/docs/start/aws/nextjs)
|
|
48
|
+
- [Remix](https://sst-community.github.io/sst/docs/start/aws/remix)
|
|
49
|
+
- [Astro](https://sst-community.github.io/sst/docs/start/aws/astro)
|
|
50
|
+
- [Hono](https://sst-community.github.io/sst/docs/start/aws/hono)
|
|
51
|
+
|
|
52
|
+
## Learn More
|
|
53
|
+
|
|
54
|
+
Learn more about some of the key concepts:
|
|
55
|
+
|
|
56
|
+
- [Live](https://sst-community.github.io/sst/docs/live)
|
|
57
|
+
- [Linking](https://sst-community.github.io/sst/docs/linking)
|
|
58
|
+
- [Console](https://sst-community.github.io/sst/docs/console)
|
|
59
|
+
- [Components](https://sst-community.github.io/sst/docs/components)
|
|
60
|
+
|
|
61
|
+
## Contributing
|
|
62
|
+
|
|
63
|
+
Bug fixes, docs and help with issues are welcome. [CONTRIBUTING.md](https://github.com/sst-community/sst/blob/main/CONTRIBUTING.md) covers the setup, what to run before a pull request, and how pull requests are merged. For questions, ask on [Discord](https://discord.gg/DQWT3WGVm2).
|
|
64
|
+
|
|
65
|
+
To vote on what gets fixed next, add a 👍 to an issue. Here are the [open issues, most-wanted first](https://github.com/sst-community/sst/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc).
|
|
66
|
+
|
|
67
|
+
## Running Locally
|
|
68
|
+
|
|
69
|
+
Run `bun run setup`. You need [Go](https://go.dev/) and [Bun](https://bun.sh/) installed.
|
|
70
|
+
|
|
71
|
+
Now you can run the CLI locally on any of the `examples/` apps.
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
cd examples/aws-api
|
|
75
|
+
go run ../../cmd/sst <command>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
If you want to build the CLI binary, run `bun run build:cli`. This will create a `sst` binary that you can use.
|
|
79
|
+
|
|
80
|
+
For building the docs, run `bun run docs:generate` and `bun run docs:dev`.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
**Found a bug or have a question about the fork?** [Open an issue](https://github.com/sst-community/sst/issues) or ask on [Discord](https://discord.gg/DQWT3WGVm2). For SST itself, see [anomalyco/sst](https://github.com/anomalyco/sst).
|
package/bin/sst.mjs
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
const require = createRequire(import.meta.url);
|
|
4
|
+
import { execFileSync } from "child_process";
|
|
5
|
+
|
|
6
|
+
let resolved = process.env.SST_BIN_PATH;
|
|
7
|
+
|
|
8
|
+
if (!resolved) {
|
|
9
|
+
// The binary packages are named after this package (see scripts/release.ts),
|
|
10
|
+
// which may be scoped, so read the name rather than assume "sst".
|
|
11
|
+
const { name: pkgName } = require("../package.json");
|
|
12
|
+
const name = `${pkgName}-${process.platform}-${process.arch}`;
|
|
13
|
+
const binary = process.platform === "win32" ? "sst.exe" : "sst";
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
resolved = require.resolve(`${name}/bin/${binary}`);
|
|
17
|
+
} catch (ex) {
|
|
18
|
+
console.error(
|
|
19
|
+
`It seems that your package manager failed to install the right version of the SST CLI for your platform. You can try manually installing the "${name}" package.`,
|
|
20
|
+
);
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
process.on("SIGINT", () => {});
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
execFileSync(resolved, process.argv.slice(2), {
|
|
29
|
+
stdio: "inherit",
|
|
30
|
+
});
|
|
31
|
+
} catch (ex) {
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Context, Hono } from "hono";
|
|
2
|
+
import { KeyLike } from "jose";
|
|
3
|
+
export type Adapter<Properties = any> = (route: AdapterRoute, options: AdapterOptions<Properties>) => void;
|
|
4
|
+
export type AdapterRoute = Hono;
|
|
5
|
+
export interface AdapterOptions<Properties> {
|
|
6
|
+
name: string;
|
|
7
|
+
algorithm: string;
|
|
8
|
+
encryption: {
|
|
9
|
+
publicKey: () => Promise<KeyLike>;
|
|
10
|
+
privateKey: () => Promise<KeyLike>;
|
|
11
|
+
};
|
|
12
|
+
signing: {
|
|
13
|
+
publicKey: () => Promise<KeyLike>;
|
|
14
|
+
privateKey: () => Promise<KeyLike>;
|
|
15
|
+
};
|
|
16
|
+
success: (ctx: Context, properties: Properties) => Promise<Response>;
|
|
17
|
+
forward: (ctx: Context, response: Response) => Response;
|
|
18
|
+
cookie: (ctx: Context, key: string, value: string, maxAge: number) => void;
|
|
19
|
+
}
|
|
20
|
+
export declare class AdapterError extends Error {
|
|
21
|
+
}
|
|
22
|
+
export declare class AdapterUnknownError extends AdapterError {
|
|
23
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { OauthBasicConfig } from "./oauth.js";
|
|
2
|
+
export declare const AppleAdapter: (config: OauthBasicConfig) => (routes: import("./adapter.js").AdapterRoute, ctx: import("./adapter.js").AdapterOptions<{
|
|
3
|
+
tokenset: import("openid-client").TokenSet;
|
|
4
|
+
client: import("openid-client").BaseClient;
|
|
5
|
+
}>) => Promise<void>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Issuer } from "openid-client";
|
|
2
|
+
import { OauthAdapter } from "./oauth.js";
|
|
3
|
+
// This adapter support the OAuth flow with the response_mode "form_post" for now.
|
|
4
|
+
// More details about the flow:
|
|
5
|
+
// https://developer.apple.com/documentation/devicemanagement/user_enrollment/onboarding_users_with_account_sign-in/implementing_the_oauth2_authentication_user-enrollment_flow
|
|
6
|
+
//
|
|
7
|
+
// Also note that Apple's discover uri does not work for the OAuth flow, as the
|
|
8
|
+
// userinfo_endpoint are not included in the response.
|
|
9
|
+
// await Issuer.discover("https://appleid.apple.com/.well-known/openid-configuration/");
|
|
10
|
+
const issuer = await Issuer.discover("https://appleid.apple.com/.well-known/openid-configuration");
|
|
11
|
+
export const AppleAdapter =
|
|
12
|
+
/* @__PURE__ */
|
|
13
|
+
(config) => {
|
|
14
|
+
return OauthAdapter({
|
|
15
|
+
issuer,
|
|
16
|
+
...config,
|
|
17
|
+
params: {
|
|
18
|
+
...config.params,
|
|
19
|
+
response_mode: "form_post",
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function CodeAdapter(config: {
|
|
2
|
+
length?: number;
|
|
3
|
+
onCodeRequest: (code: string, claims: Record<string, any>, req: Request) => Promise<Response>;
|
|
4
|
+
onCodeInvalid: (code: string, claims: Record<string, any>, req: Request) => Promise<Response>;
|
|
5
|
+
}): (routes: import("./adapter.js").AdapterRoute, ctx: import("./adapter.js").AdapterOptions<{
|
|
6
|
+
claims: Record<string, string>;
|
|
7
|
+
}>) => void;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { deleteCookie, getCookie } from "hono/cookie";
|
|
2
|
+
import { UnknownStateError } from "../index.js";
|
|
3
|
+
import { CompactEncrypt, compactDecrypt } from "jose";
|
|
4
|
+
export function CodeAdapter(config) {
|
|
5
|
+
const length = config.length || 6;
|
|
6
|
+
function generate() {
|
|
7
|
+
const buffer = crypto.getRandomValues(new Uint8Array(length));
|
|
8
|
+
const otp = Array.from(buffer)
|
|
9
|
+
.map((byte) => byte % 10)
|
|
10
|
+
.join("");
|
|
11
|
+
return otp;
|
|
12
|
+
}
|
|
13
|
+
return function (routes, ctx) {
|
|
14
|
+
routes.get("/authorize", async (c) => {
|
|
15
|
+
const code = generate();
|
|
16
|
+
const claims = c.req.query();
|
|
17
|
+
delete claims["client_id"];
|
|
18
|
+
delete claims["redirect_uri"];
|
|
19
|
+
delete claims["response_type"];
|
|
20
|
+
delete claims["provider"];
|
|
21
|
+
const authorization = await new CompactEncrypt(new TextEncoder().encode(JSON.stringify({
|
|
22
|
+
claims,
|
|
23
|
+
code,
|
|
24
|
+
})))
|
|
25
|
+
.setProtectedHeader({ alg: "RSA-OAEP-512", enc: "A256GCM" })
|
|
26
|
+
.encrypt(await ctx.encryption.publicKey());
|
|
27
|
+
ctx.cookie(c, "authorization", authorization, 60 * 10);
|
|
28
|
+
return ctx.forward(c, await config.onCodeRequest(code, claims, c.req.raw));
|
|
29
|
+
});
|
|
30
|
+
routes.get("/callback", async (c) => {
|
|
31
|
+
const authorization = getCookie(c, "authorization");
|
|
32
|
+
if (!authorization)
|
|
33
|
+
throw new UnknownStateError();
|
|
34
|
+
const { code, claims } = JSON.parse(new TextDecoder().decode(await compactDecrypt(authorization, await ctx.encryption.privateKey()).then((value) => value.plaintext)));
|
|
35
|
+
if (!code || !claims) {
|
|
36
|
+
return ctx.forward(c, await config.onCodeInvalid(code, claims, c.req.raw));
|
|
37
|
+
}
|
|
38
|
+
const compare = c.req.query("code");
|
|
39
|
+
console.log("comparing", code, "to", compare);
|
|
40
|
+
if (code !== compare) {
|
|
41
|
+
return ctx.forward(c, await config.onCodeInvalid(code, claims, c.req.raw));
|
|
42
|
+
}
|
|
43
|
+
deleteCookie(c, "authorization");
|
|
44
|
+
return ctx.forward(c, await ctx.success(c, { claims }));
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { OauthBasicConfig } from "./oauth.js";
|
|
2
|
+
export declare const FacebookAdapter: (config: OauthBasicConfig) => (routes: import("./adapter.js").AdapterRoute, ctx: import("./adapter.js").AdapterOptions<{
|
|
3
|
+
tokenset: import("openid-client").TokenSet;
|
|
4
|
+
client: import("openid-client").BaseClient;
|
|
5
|
+
}>) => Promise<void>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Issuer } from "openid-client";
|
|
2
|
+
import { OauthAdapter } from "./oauth.js";
|
|
3
|
+
// Facebook's OIDC flow returns "id_token" as uri hash in redirect uri. Hashes
|
|
4
|
+
// are not passed to Lambda event object. It is likely that Facebook only wants
|
|
5
|
+
// to support redirecting to a frontend uri.
|
|
6
|
+
//
|
|
7
|
+
// We are only going to support the OAuth flow for now. More details about the flow:
|
|
8
|
+
// https://developers.facebook.com/docs/facebook-login/guides/advanced/oidc-token
|
|
9
|
+
//
|
|
10
|
+
// Also note that Facebook's discover uri does not work for the OAuth flow, as the
|
|
11
|
+
// token_endpoint and userinfo_endpoint are not included in the response.
|
|
12
|
+
// await Issuer.discover("https://www.facebook.com/.well-known/openid-configuration/");
|
|
13
|
+
const issuer = new Issuer({
|
|
14
|
+
issuer: "https://www.facebook.com",
|
|
15
|
+
authorization_endpoint: "https://facebook.com/dialog/oauth/",
|
|
16
|
+
jwks_uri: "https://www.facebook.com/.well-known/oauth/openid/jwks/",
|
|
17
|
+
token_endpoint: "https://graph.facebook.com/oauth/access_token",
|
|
18
|
+
userinfo_endpoint: "https://graph.facebook.com/oauth/access_token",
|
|
19
|
+
});
|
|
20
|
+
export const FacebookAdapter =
|
|
21
|
+
/* @__PURE__ */
|
|
22
|
+
(config) => {
|
|
23
|
+
return OauthAdapter({
|
|
24
|
+
issuer,
|
|
25
|
+
...config,
|
|
26
|
+
});
|
|
27
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { OauthBasicConfig } from "./oauth.js";
|
|
2
|
+
type Config = OauthBasicConfig;
|
|
3
|
+
export declare const GithubAdapter: (config: Config) => (routes: import("./adapter.js").AdapterRoute, ctx: import("./adapter.js").AdapterOptions<{
|
|
4
|
+
tokenset: import("openid-client").TokenSet;
|
|
5
|
+
client: import("openid-client").BaseClient;
|
|
6
|
+
}>) => Promise<void>;
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Issuer } from "openid-client";
|
|
2
|
+
import { OauthAdapter } from "./oauth.js";
|
|
3
|
+
const issuer = new Issuer({
|
|
4
|
+
issuer: "https://github.com",
|
|
5
|
+
authorization_endpoint: "https://github.com/login/oauth/authorize",
|
|
6
|
+
token_endpoint: "https://github.com/login/oauth/access_token",
|
|
7
|
+
});
|
|
8
|
+
export const GithubAdapter =
|
|
9
|
+
/* @__PURE__ */
|
|
10
|
+
(config) => {
|
|
11
|
+
return OauthAdapter({
|
|
12
|
+
issuer,
|
|
13
|
+
...config,
|
|
14
|
+
});
|
|
15
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { OidcBasicConfig } from "./oidc.js";
|
|
2
|
+
import { OauthBasicConfig } from "./oauth.js";
|
|
3
|
+
type GooglePrompt = "none" | "consent" | "select_account";
|
|
4
|
+
type GoogleAccessType = "offline" | "online";
|
|
5
|
+
type GoogleConfig = (OauthBasicConfig & {
|
|
6
|
+
mode: "oauth";
|
|
7
|
+
prompt?: GooglePrompt;
|
|
8
|
+
accessType?: GoogleAccessType;
|
|
9
|
+
}) | (OidcBasicConfig & {
|
|
10
|
+
mode: "oidc";
|
|
11
|
+
prompt?: GooglePrompt;
|
|
12
|
+
});
|
|
13
|
+
export declare function GoogleAdapter(config: GoogleConfig): (routes: import("./adapter.js").AdapterRoute, ctx: import("./adapter.js").AdapterOptions<{
|
|
14
|
+
tokenset: import("openid-client").TokenSet;
|
|
15
|
+
client: import("openid-client").BaseClient;
|
|
16
|
+
}>) => Promise<void>;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Issuer } from "openid-client";
|
|
2
|
+
import { OidcAdapter } from "./oidc.js";
|
|
3
|
+
import { OauthAdapter } from "./oauth.js";
|
|
4
|
+
const issuer = await Issuer.discover("https://accounts.google.com");
|
|
5
|
+
export function GoogleAdapter(config) {
|
|
6
|
+
/* @__PURE__ */
|
|
7
|
+
if (config.mode === "oauth") {
|
|
8
|
+
return OauthAdapter({
|
|
9
|
+
issuer,
|
|
10
|
+
...config,
|
|
11
|
+
params: {
|
|
12
|
+
...(config.accessType && { access_type: config.accessType }),
|
|
13
|
+
...config.params,
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return OidcAdapter({
|
|
18
|
+
issuer,
|
|
19
|
+
scope: "openid email profile",
|
|
20
|
+
...config,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from "./oidc.js";
|
|
2
|
+
export * from "./google.js";
|
|
3
|
+
export * from "./link.js";
|
|
4
|
+
export * from "./github.js";
|
|
5
|
+
export * from "./facebook.js";
|
|
6
|
+
export * from "./microsoft.js";
|
|
7
|
+
export * from "./oauth.js";
|
|
8
|
+
export * from "./spotify.js";
|
|
9
|
+
export * from "./code.js";
|
|
10
|
+
export * from "./apple.js";
|
|
11
|
+
export type { Adapter } from "./adapter.js";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from "./oidc.js";
|
|
2
|
+
export * from "./google.js";
|
|
3
|
+
export * from "./link.js";
|
|
4
|
+
export * from "./github.js";
|
|
5
|
+
export * from "./facebook.js";
|
|
6
|
+
export * from "./microsoft.js";
|
|
7
|
+
export * from "./oauth.js";
|
|
8
|
+
export * from "./spotify.js";
|
|
9
|
+
export * from "./code.js";
|
|
10
|
+
export * from "./apple.js";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { SignJWT, jwtVerify } from "jose";
|
|
2
|
+
export function LinkAdapter(config) {
|
|
3
|
+
return function (routes, ctx) {
|
|
4
|
+
routes.get("/authorize", async (c) => {
|
|
5
|
+
const token = await new SignJWT(c.req.query())
|
|
6
|
+
.setProtectedHeader({ alg: ctx.algorithm })
|
|
7
|
+
.setExpirationTime("10m")
|
|
8
|
+
.sign(await ctx.signing.privateKey());
|
|
9
|
+
const url = new URL(new URL(c.req.url).origin);
|
|
10
|
+
url.pathname = `/${ctx.name}/callback`;
|
|
11
|
+
for (const key of url.searchParams.keys()) {
|
|
12
|
+
url.searchParams.delete(key);
|
|
13
|
+
}
|
|
14
|
+
url.searchParams.set("token", token);
|
|
15
|
+
const resp = ctx.forward(c, await config.onLink(url.toString(), c.req.query()));
|
|
16
|
+
return resp;
|
|
17
|
+
});
|
|
18
|
+
routes.get("/callback", async (c) => {
|
|
19
|
+
const token = c.req.query("token");
|
|
20
|
+
if (!token)
|
|
21
|
+
throw new Error("Missing token parameter");
|
|
22
|
+
const verified = await jwtVerify(token, await ctx.signing.publicKey());
|
|
23
|
+
const resp = await ctx.success(c, { claims: verified.payload });
|
|
24
|
+
return resp;
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { OidcBasicConfig } from "./oidc.js";
|
|
2
|
+
type MicrosoftConfig = OidcBasicConfig & {
|
|
3
|
+
mode: "oidc";
|
|
4
|
+
prompt?: "login" | "none" | "consent" | "select_account";
|
|
5
|
+
tenantID?: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function MicrosoftAdapter(config: MicrosoftConfig): (routes: import("./adapter.js").AdapterRoute, ctx: import("./adapter.js").AdapterOptions<{
|
|
8
|
+
tokenset: import("openid-client").TokenSet;
|
|
9
|
+
client: import("openid-client").BaseClient;
|
|
10
|
+
}>) => Promise<void>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Issuer } from "openid-client";
|
|
2
|
+
import { OidcAdapter } from "./oidc.js";
|
|
3
|
+
export function MicrosoftAdapter(config) {
|
|
4
|
+
const authority = config?.tenantID ?? "common";
|
|
5
|
+
const issuer = `https://login.microsoftonline.com/${authority}`;
|
|
6
|
+
return OidcAdapter({
|
|
7
|
+
issuer: new Issuer({
|
|
8
|
+
issuer: `${issuer}/v2.0`,
|
|
9
|
+
authorization_endpoint: `${issuer}/oauth2/v2.0/authorize`,
|
|
10
|
+
token_endpoint: `${issuer}/oauth2/v2.0/token`,
|
|
11
|
+
jwks_uri: `${issuer}/discovery/v2.0/keys`,
|
|
12
|
+
}),
|
|
13
|
+
scope: "openid email profile",
|
|
14
|
+
...config,
|
|
15
|
+
});
|
|
16
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { BaseClient, Issuer, TokenSet } from "openid-client";
|
|
2
|
+
import { AdapterError } from "./adapter.js";
|
|
3
|
+
export interface OauthBasicConfig {
|
|
4
|
+
/**
|
|
5
|
+
* The clientID provided by the third party oauth service
|
|
6
|
+
*/
|
|
7
|
+
clientID: string;
|
|
8
|
+
/**
|
|
9
|
+
* The clientSecret provided by the third party oauth service
|
|
10
|
+
*/
|
|
11
|
+
clientSecret: string;
|
|
12
|
+
/**
|
|
13
|
+
* Various scopes requested for the access token
|
|
14
|
+
*/
|
|
15
|
+
scope: string;
|
|
16
|
+
/**
|
|
17
|
+
* Determines whether users will be prompted for reauthentication and consent
|
|
18
|
+
*/
|
|
19
|
+
prompt?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Additional parameters to be passed to the authorization endpoint
|
|
22
|
+
*/
|
|
23
|
+
params?: Record<string, string>;
|
|
24
|
+
}
|
|
25
|
+
export interface OauthConfig extends OauthBasicConfig {
|
|
26
|
+
issuer: Issuer;
|
|
27
|
+
}
|
|
28
|
+
export declare class OauthError extends AdapterError {
|
|
29
|
+
}
|
|
30
|
+
export declare const OauthAdapter: (config: OauthConfig) => (routes: import("./adapter.js").AdapterRoute, ctx: import("./adapter.js").AdapterOptions<{
|
|
31
|
+
tokenset: TokenSet;
|
|
32
|
+
client: BaseClient;
|
|
33
|
+
}>) => Promise<void>;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { generators } from "openid-client";
|
|
2
|
+
import { AdapterError } from "./adapter.js";
|
|
3
|
+
import { getCookie } from "hono/cookie";
|
|
4
|
+
export class OauthError extends AdapterError {
|
|
5
|
+
}
|
|
6
|
+
export const OauthAdapter =
|
|
7
|
+
/* @__PURE__ */
|
|
8
|
+
(config) => {
|
|
9
|
+
return async function (routes, ctx) {
|
|
10
|
+
function getClient(c) {
|
|
11
|
+
const callback = new URL(c.req.url);
|
|
12
|
+
callback.pathname = callback.pathname.replace(/authorize.*$/, "callback");
|
|
13
|
+
callback.search = "";
|
|
14
|
+
callback.host = c.req.header("x-forwarded-host") || callback.host;
|
|
15
|
+
return [
|
|
16
|
+
callback,
|
|
17
|
+
new config.issuer.Client({
|
|
18
|
+
client_id: config.clientID,
|
|
19
|
+
client_secret: config.clientSecret,
|
|
20
|
+
redirect_uris: [callback.toString()],
|
|
21
|
+
response_types: ["code"],
|
|
22
|
+
}),
|
|
23
|
+
];
|
|
24
|
+
}
|
|
25
|
+
routes.get("/authorize", async (c) => {
|
|
26
|
+
const [_, client] = getClient(c);
|
|
27
|
+
const code_verifier = generators.codeVerifier();
|
|
28
|
+
const state = generators.state();
|
|
29
|
+
const code_challenge = generators.codeChallenge(code_verifier);
|
|
30
|
+
const url = client.authorizationUrl({
|
|
31
|
+
scope: config.scope,
|
|
32
|
+
code_challenge: code_challenge,
|
|
33
|
+
code_challenge_method: "S256",
|
|
34
|
+
state,
|
|
35
|
+
prompt: config.prompt,
|
|
36
|
+
...config.params,
|
|
37
|
+
});
|
|
38
|
+
ctx.cookie(c, "auth_code_verifier", code_verifier, 60 * 10);
|
|
39
|
+
ctx.cookie(c, "auth_state", state, 60 * 10);
|
|
40
|
+
return c.redirect(url);
|
|
41
|
+
});
|
|
42
|
+
routes.get("/callback", async (c) => {
|
|
43
|
+
const [callback, client] = getClient(c);
|
|
44
|
+
const query = c.req.query();
|
|
45
|
+
if (query.error) {
|
|
46
|
+
throw new OauthError(query.error);
|
|
47
|
+
}
|
|
48
|
+
const code_verifier = getCookie(c, "auth_code_verifier");
|
|
49
|
+
const state = getCookie(c, "auth_state");
|
|
50
|
+
const tokenset = await client[config.issuer.metadata.userinfo_endpoint
|
|
51
|
+
? "callback"
|
|
52
|
+
: "oauthCallback"](callback.toString(), query, {
|
|
53
|
+
code_verifier,
|
|
54
|
+
state,
|
|
55
|
+
});
|
|
56
|
+
return ctx.success(c, {
|
|
57
|
+
client,
|
|
58
|
+
tokenset,
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
// response_mode=form_post
|
|
62
|
+
routes.post("/callback", async (c) => {
|
|
63
|
+
const [callback, client] = getClient(c);
|
|
64
|
+
const form = await c.req.formData();
|
|
65
|
+
if (form.get("error")) {
|
|
66
|
+
throw new OauthError(form.get("error").toString());
|
|
67
|
+
}
|
|
68
|
+
const code_verifier = getCookie(c, "auth_code_verifier");
|
|
69
|
+
const state = getCookie(c, "auth_state");
|
|
70
|
+
const tokenset = await client[config.issuer.metadata.userinfo_endpoint
|
|
71
|
+
? "callback"
|
|
72
|
+
: "oauthCallback"](callback.toString(), Object.fromEntries(form), {
|
|
73
|
+
code_verifier,
|
|
74
|
+
state,
|
|
75
|
+
});
|
|
76
|
+
return ctx.success(c, {
|
|
77
|
+
client,
|
|
78
|
+
tokenset,
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
};
|
|
82
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { BaseClient, Issuer, TokenSet } from "openid-client";
|
|
2
|
+
export interface OidcBasicConfig {
|
|
3
|
+
/**
|
|
4
|
+
* The clientID provided by the third party oauth service
|
|
5
|
+
*/
|
|
6
|
+
clientID: string;
|
|
7
|
+
/**
|
|
8
|
+
* Determines whether users will be prompted for reauthentication and consent
|
|
9
|
+
*/
|
|
10
|
+
prompt?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface OidcConfig extends OidcBasicConfig {
|
|
13
|
+
issuer: Issuer;
|
|
14
|
+
scope: string;
|
|
15
|
+
}
|
|
16
|
+
export declare const OidcAdapter: (config: OidcConfig) => (routes: import("./adapter.js").AdapterRoute, ctx: import("./adapter.js").AdapterOptions<{
|
|
17
|
+
tokenset: TokenSet;
|
|
18
|
+
client: BaseClient;
|
|
19
|
+
}>) => Promise<void>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { generators } from "openid-client";
|
|
2
|
+
import { getCookie } from "hono/cookie";
|
|
3
|
+
export const OidcAdapter = /* @__PURE__ */ (config) => {
|
|
4
|
+
return async function (routes, ctx) {
|
|
5
|
+
routes.get("/authorize", async (c) => {
|
|
6
|
+
const callback = new URL(c.req.url);
|
|
7
|
+
callback.pathname = callback.pathname.replace(/authorize.*$/, "callback");
|
|
8
|
+
callback.search = "";
|
|
9
|
+
callback.host = c.req.header("x-forwarded-host") || callback.host;
|
|
10
|
+
const client = new config.issuer.Client({
|
|
11
|
+
client_id: config.clientID,
|
|
12
|
+
redirect_uris: [callback.toString()],
|
|
13
|
+
response_types: ["id_token"],
|
|
14
|
+
});
|
|
15
|
+
const nonce = generators.nonce();
|
|
16
|
+
const state = generators.state();
|
|
17
|
+
const url = client.authorizationUrl({
|
|
18
|
+
scope: config.scope,
|
|
19
|
+
response_mode: "form_post",
|
|
20
|
+
nonce,
|
|
21
|
+
state,
|
|
22
|
+
prompt: config.prompt,
|
|
23
|
+
});
|
|
24
|
+
ctx.cookie(c, "auth_nonce", nonce, 60 * 10);
|
|
25
|
+
ctx.cookie(c, "auth_state", state, 60 * 10);
|
|
26
|
+
return c.redirect(url);
|
|
27
|
+
});
|
|
28
|
+
routes.post("/callback", async (c) => {
|
|
29
|
+
const callback = c.req.url.replace(/authorize\/.*$/, "callback");
|
|
30
|
+
const client = new config.issuer.Client({
|
|
31
|
+
client_id: config.clientID,
|
|
32
|
+
redirect_uris: [callback],
|
|
33
|
+
response_types: ["id_token"],
|
|
34
|
+
});
|
|
35
|
+
const form = await c.req.formData();
|
|
36
|
+
const nonce = getCookie(c, "auth_nonce");
|
|
37
|
+
const state = getCookie(c, "auth_state");
|
|
38
|
+
const tokenset = await client.callback(callback, Object.fromEntries(form), {
|
|
39
|
+
nonce,
|
|
40
|
+
state,
|
|
41
|
+
});
|
|
42
|
+
return ctx.success(c, {
|
|
43
|
+
tokenset,
|
|
44
|
+
client,
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { OauthBasicConfig } from "./oauth.js";
|
|
2
|
+
/**
|
|
3
|
+
* The Spotify Adapter follows the PKCE flow outlined here:
|
|
4
|
+
* https://developer.spotify.com/documentation/web-api/tutorials/code-pkce-flow
|
|
5
|
+
*
|
|
6
|
+
* List of scopes available:
|
|
7
|
+
* https://developer.spotify.com/documentation/web-api/concepts/scopes
|
|
8
|
+
*/
|
|
9
|
+
export declare const SpotifyAdapter: (config: OauthBasicConfig) => (routes: import("./adapter.js").AdapterRoute, ctx: import("./adapter.js").AdapterOptions<{
|
|
10
|
+
tokenset: import("openid-client").TokenSet;
|
|
11
|
+
client: import("openid-client").BaseClient;
|
|
12
|
+
}>) => Promise<void>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Issuer } from "openid-client";
|
|
2
|
+
import { OauthAdapter } from "./oauth.js";
|
|
3
|
+
const issuer = new Issuer({
|
|
4
|
+
issuer: "https://accounts.spotify.com",
|
|
5
|
+
authorization_endpoint: "https://accounts.spotify.com/authorize",
|
|
6
|
+
token_endpoint: "https://accounts.spotify.com/api/token",
|
|
7
|
+
});
|
|
8
|
+
/**
|
|
9
|
+
* The Spotify Adapter follows the PKCE flow outlined here:
|
|
10
|
+
* https://developer.spotify.com/documentation/web-api/tutorials/code-pkce-flow
|
|
11
|
+
*
|
|
12
|
+
* List of scopes available:
|
|
13
|
+
* https://developer.spotify.com/documentation/web-api/concepts/scopes
|
|
14
|
+
*/
|
|
15
|
+
export const SpotifyAdapter =
|
|
16
|
+
/* @__PURE__ */
|
|
17
|
+
(config) => {
|
|
18
|
+
return OauthAdapter({
|
|
19
|
+
issuer,
|
|
20
|
+
...config,
|
|
21
|
+
});
|
|
22
|
+
};
|