antarctic 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +24 -0
- package/README.md +155 -0
- package/dist/auth.d.ts +63 -0
- package/dist/auth.js +191 -0
- package/dist/client.d.ts +16 -0
- package/dist/client.js +105 -0
- package/dist/index.d.ts +70 -0
- package/dist/index.js +69 -0
- package/dist/oauth2.d.ts +16 -0
- package/dist/oauth2.js +67 -0
- package/dist/oidc.d.ts +1 -0
- package/dist/oidc.js +11 -0
- package/dist/providers/42.d.ts +16 -0
- package/dist/providers/42.js +75 -0
- package/dist/providers/amazon-cognito.d.ts +21 -0
- package/dist/providers/amazon-cognito.js +76 -0
- package/dist/providers/anilist.d.ts +14 -0
- package/dist/providers/anilist.js +91 -0
- package/dist/providers/apple.d.ts +22 -0
- package/dist/providers/apple.js +106 -0
- package/dist/providers/atlassian.d.ts +17 -0
- package/dist/providers/atlassian.js +83 -0
- package/dist/providers/auth0.d.ts +21 -0
- package/dist/providers/auth0.js +80 -0
- package/dist/providers/authentik.d.ts +21 -0
- package/dist/providers/authentik.js +75 -0
- package/dist/providers/autodesk.d.ts +16 -0
- package/dist/providers/autodesk.js +60 -0
- package/dist/providers/battlenet.d.ts +16 -0
- package/dist/providers/battlenet.js +69 -0
- package/dist/providers/bitbucket.d.ts +15 -0
- package/dist/providers/bitbucket.js +79 -0
- package/dist/providers/box.d.ts +18 -0
- package/dist/providers/box.js +90 -0
- package/dist/providers/bungie.d.ts +17 -0
- package/dist/providers/bungie.js +72 -0
- package/dist/providers/coinbase.d.ts +18 -0
- package/dist/providers/coinbase.js +95 -0
- package/dist/providers/discord.d.ts +16 -0
- package/dist/providers/discord.js +68 -0
- package/dist/providers/donation-alerts.d.ts +17 -0
- package/dist/providers/donation-alerts.js +86 -0
- package/dist/providers/dribbble.d.ts +16 -0
- package/dist/providers/dribbble.js +73 -0
- package/dist/providers/dropbox.d.ts +16 -0
- package/dist/providers/dropbox.js +96 -0
- package/dist/providers/epicgames.d.ts +16 -0
- package/dist/providers/epicgames.js +60 -0
- package/dist/providers/etsy.d.ts +15 -0
- package/dist/providers/etsy.js +68 -0
- package/dist/providers/facebook.d.ts +16 -0
- package/dist/providers/facebook.js +80 -0
- package/dist/providers/figma.d.ts +15 -0
- package/dist/providers/figma.js +56 -0
- package/dist/providers/gitea.d.ts +19 -0
- package/dist/providers/gitea.js +62 -0
- package/dist/providers/github.d.ts +17 -0
- package/dist/providers/github.js +142 -0
- package/dist/providers/gitlab.d.ts +21 -0
- package/dist/providers/gitlab.js +64 -0
- package/dist/providers/google.d.ts +16 -0
- package/dist/providers/google.js +70 -0
- package/dist/providers/intuit.d.ts +16 -0
- package/dist/providers/intuit.js +66 -0
- package/dist/providers/kakao.d.ts +17 -0
- package/dist/providers/kakao.js +95 -0
- package/dist/providers/keycloak.d.ts +21 -0
- package/dist/providers/keycloak.js +74 -0
- package/dist/providers/kick.d.ts +18 -0
- package/dist/providers/kick.js +103 -0
- package/dist/providers/lichess.d.ts +14 -0
- package/dist/providers/lichess.js +63 -0
- package/dist/providers/line.d.ts +17 -0
- package/dist/providers/line.js +98 -0
- package/dist/providers/linear.d.ts +16 -0
- package/dist/providers/linear.js +112 -0
- package/dist/providers/linkedin.d.ts +17 -0
- package/dist/providers/linkedin.js +89 -0
- package/dist/providers/mastodon.d.ts +20 -0
- package/dist/providers/mastodon.js +67 -0
- package/dist/providers/mercadolibre.d.ts +17 -0
- package/dist/providers/mercadolibre.js +95 -0
- package/dist/providers/mercadopago.d.ts +17 -0
- package/dist/providers/mercadopago.js +95 -0
- package/dist/providers/microsoft-entra-id.d.ts +20 -0
- package/dist/providers/microsoft-entra-id.js +120 -0
- package/dist/providers/myanimelist.d.ts +15 -0
- package/dist/providers/myanimelist.js +56 -0
- package/dist/providers/naver.d.ts +17 -0
- package/dist/providers/naver.js +83 -0
- package/dist/providers/notion.d.ts +14 -0
- package/dist/providers/notion.js +65 -0
- package/dist/providers/okta.d.ts +22 -0
- package/dist/providers/okta.js +85 -0
- package/dist/providers/osu.d.ts +17 -0
- package/dist/providers/osu.js +82 -0
- package/dist/providers/patreon.d.ts +17 -0
- package/dist/providers/patreon.js +89 -0
- package/dist/providers/polar.d.ts +18 -0
- package/dist/providers/polar.js +100 -0
- package/dist/providers/reddit.d.ts +15 -0
- package/dist/providers/reddit.js +55 -0
- package/dist/providers/roblox.d.ts +16 -0
- package/dist/providers/roblox.js +67 -0
- package/dist/providers/salesforce.d.ts +21 -0
- package/dist/providers/salesforce.js +74 -0
- package/dist/providers/shikimori.d.ts +17 -0
- package/dist/providers/shikimori.js +81 -0
- package/dist/providers/slack.d.ts +14 -0
- package/dist/providers/slack.js +59 -0
- package/dist/providers/spotify.d.ts +15 -0
- package/dist/providers/spotify.js +67 -0
- package/dist/providers/startgg.d.ts +17 -0
- package/dist/providers/startgg.js +143 -0
- package/dist/providers/strava.d.ts +17 -0
- package/dist/providers/strava.js +85 -0
- package/dist/providers/synology.d.ts +9 -0
- package/dist/providers/synology.js +20 -0
- package/dist/providers/tiktok.d.ts +18 -0
- package/dist/providers/tiktok.js +109 -0
- package/dist/providers/tiltify.d.ts +17 -0
- package/dist/providers/tiltify.js +90 -0
- package/dist/providers/tumblr.d.ts +15 -0
- package/dist/providers/tumblr.js +63 -0
- package/dist/providers/twitch.d.ts +17 -0
- package/dist/providers/twitch.js +91 -0
- package/dist/providers/twitter.d.ts +16 -0
- package/dist/providers/twitter.js +67 -0
- package/dist/providers/vk.d.ts +16 -0
- package/dist/providers/vk.js +82 -0
- package/dist/providers/withings.d.ts +16 -0
- package/dist/providers/withings.js +120 -0
- package/dist/providers/workos.d.ts +16 -0
- package/dist/providers/workos.js +82 -0
- package/dist/providers/yahoo.d.ts +15 -0
- package/dist/providers/yahoo.js +65 -0
- package/dist/providers/yandex.d.ts +15 -0
- package/dist/providers/yandex.js +66 -0
- package/dist/providers/zoom.d.ts +16 -0
- package/dist/providers/zoom.js +66 -0
- package/dist/request.d.ts +26 -0
- package/dist/request.js +182 -0
- package/dist/utils.d.ts +2 -0
- package/dist/utils.js +20 -0
- package/package.json +42 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { OAuth2Tokens } from "../oauth2.js";
|
|
2
|
+
import type { OAuthCallbackQuery, OAuthUser, ProviderOptions } from "../auth.js";
|
|
3
|
+
export interface MicrosoftEntraIdOptions extends ProviderOptions {
|
|
4
|
+
tenant?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare class MicrosoftEntraId {
|
|
7
|
+
private authorizationEndpoint;
|
|
8
|
+
private tokenEndpoint;
|
|
9
|
+
private clientId;
|
|
10
|
+
private clientSecret;
|
|
11
|
+
private redirectURI;
|
|
12
|
+
private auth;
|
|
13
|
+
constructor(options: MicrosoftEntraIdOptions);
|
|
14
|
+
constructor(tenant: string, clientId: string, clientSecret: string | null, redirectURI: string);
|
|
15
|
+
createAuthorizationURL(state: string, codeVerifier: string, scopes: string[]): URL;
|
|
16
|
+
validateAuthorizationCode(code: string, codeVerifier: string): Promise<OAuth2Tokens>;
|
|
17
|
+
refreshAccessToken(refreshToken: string, scopes: string[]): Promise<OAuth2Tokens>;
|
|
18
|
+
getAuthorizationURL(scopes?: string[]): Promise<URL>;
|
|
19
|
+
getUser(query: OAuthCallbackQuery): Promise<OAuthUser>;
|
|
20
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { createS256CodeChallenge } from "../oauth2.js";
|
|
2
|
+
import { createOAuth2Request, encodeBasicCredentials, joinURIAndPath, sendTokenRequest } from "../request.js";
|
|
3
|
+
import { decodeIdToken } from "../oidc.js";
|
|
4
|
+
import { consumeOAuthState, fetchUserProfile, generateOAuthCodeVerifier, generateOAuthState, InvalidOAuthCallbackError, parseCallbackQuery, profileId, profileString, requireAuthConfig, requireProviderOption, resolveAuthConfig, resolveScopes, saveOAuthState } from "../auth.js";
|
|
5
|
+
const userinfoEndpoint = "https://graph.microsoft.com/oidc/userinfo";
|
|
6
|
+
const envPrefix = "MICROSOFT_ENTRA_ID";
|
|
7
|
+
const defaultScopes = ["openid", "profile", "email"];
|
|
8
|
+
export class MicrosoftEntraId {
|
|
9
|
+
authorizationEndpoint;
|
|
10
|
+
tokenEndpoint;
|
|
11
|
+
clientId;
|
|
12
|
+
clientSecret;
|
|
13
|
+
redirectURI;
|
|
14
|
+
auth = null;
|
|
15
|
+
constructor(tenantOrOptions, clientId, clientSecret, redirectURI) {
|
|
16
|
+
let tenant;
|
|
17
|
+
if (typeof tenantOrOptions === "object") {
|
|
18
|
+
this.auth = resolveAuthConfig(envPrefix, tenantOrOptions, { redirectURI: true });
|
|
19
|
+
tenant = requireProviderOption(tenantOrOptions.tenant, envPrefix, "TENANT", "tenant");
|
|
20
|
+
this.clientId = this.auth.clientId;
|
|
21
|
+
this.clientSecret = this.auth.clientSecret;
|
|
22
|
+
this.redirectURI = this.auth.redirectURI ?? "";
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
tenant = tenantOrOptions;
|
|
26
|
+
this.clientId = clientId ?? "";
|
|
27
|
+
this.clientSecret = clientSecret ?? null;
|
|
28
|
+
this.redirectURI = redirectURI ?? "";
|
|
29
|
+
}
|
|
30
|
+
this.authorizationEndpoint = joinURIAndPath("https://login.microsoftonline.com", tenant, "/oauth2/v2.0/authorize");
|
|
31
|
+
this.tokenEndpoint = joinURIAndPath("https://login.microsoftonline.com", tenant, "/oauth2/v2.0/token");
|
|
32
|
+
}
|
|
33
|
+
createAuthorizationURL(state, codeVerifier, scopes) {
|
|
34
|
+
const url = new URL(this.authorizationEndpoint);
|
|
35
|
+
url.searchParams.set("response_type", "code");
|
|
36
|
+
url.searchParams.set("client_id", this.clientId);
|
|
37
|
+
url.searchParams.set("redirect_uri", this.redirectURI);
|
|
38
|
+
url.searchParams.set("state", state);
|
|
39
|
+
const codeChallenge = createS256CodeChallenge(codeVerifier);
|
|
40
|
+
url.searchParams.set("code_challenge_method", "S256");
|
|
41
|
+
url.searchParams.set("code_challenge", codeChallenge);
|
|
42
|
+
if (scopes.length > 0) {
|
|
43
|
+
url.searchParams.set("scope", scopes.join(" "));
|
|
44
|
+
}
|
|
45
|
+
return url;
|
|
46
|
+
}
|
|
47
|
+
async validateAuthorizationCode(code, codeVerifier) {
|
|
48
|
+
const body = new URLSearchParams();
|
|
49
|
+
body.set("grant_type", "authorization_code");
|
|
50
|
+
body.set("code", code);
|
|
51
|
+
body.set("redirect_uri", this.redirectURI);
|
|
52
|
+
body.set("code_verifier", codeVerifier);
|
|
53
|
+
if (this.clientSecret === null) {
|
|
54
|
+
body.set("client_id", this.clientId);
|
|
55
|
+
}
|
|
56
|
+
const request = createOAuth2Request(this.tokenEndpoint, body);
|
|
57
|
+
if (this.clientSecret !== null) {
|
|
58
|
+
const encodedCredentials = encodeBasicCredentials(this.clientId, this.clientSecret);
|
|
59
|
+
request.headers.set("Authorization", `Basic ${encodedCredentials}`);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
// Origin header required for public clients. Value can be anything.
|
|
63
|
+
request.headers.set("Origin", "arctic");
|
|
64
|
+
}
|
|
65
|
+
const tokens = await sendTokenRequest(request);
|
|
66
|
+
return tokens;
|
|
67
|
+
}
|
|
68
|
+
async refreshAccessToken(refreshToken, scopes) {
|
|
69
|
+
const body = new URLSearchParams();
|
|
70
|
+
body.set("grant_type", "refresh_token");
|
|
71
|
+
body.set("refresh_token", refreshToken);
|
|
72
|
+
if (this.clientSecret === null) {
|
|
73
|
+
body.set("client_id", this.clientId);
|
|
74
|
+
}
|
|
75
|
+
if (scopes.length > 0) {
|
|
76
|
+
body.set("scope", scopes.join(" "));
|
|
77
|
+
}
|
|
78
|
+
const request = createOAuth2Request(this.tokenEndpoint, body);
|
|
79
|
+
if (this.clientSecret !== null) {
|
|
80
|
+
const encodedCredentials = encodeBasicCredentials(this.clientId, this.clientSecret);
|
|
81
|
+
request.headers.set("Authorization", `Basic ${encodedCredentials}`);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
// Origin header required for public clients. Value can be anything.
|
|
85
|
+
request.headers.set("Origin", "arctic");
|
|
86
|
+
}
|
|
87
|
+
const tokens = await sendTokenRequest(request);
|
|
88
|
+
return tokens;
|
|
89
|
+
}
|
|
90
|
+
async getAuthorizationURL(scopes) {
|
|
91
|
+
const auth = requireAuthConfig(this.auth);
|
|
92
|
+
const state = generateOAuthState();
|
|
93
|
+
const codeVerifier = generateOAuthCodeVerifier();
|
|
94
|
+
const url = this.createAuthorizationURL(state, codeVerifier, resolveScopes(scopes, auth, defaultScopes));
|
|
95
|
+
await saveOAuthState(auth.store, state, { codeVerifier });
|
|
96
|
+
return url;
|
|
97
|
+
}
|
|
98
|
+
async getUser(query) {
|
|
99
|
+
const auth = requireAuthConfig(this.auth);
|
|
100
|
+
const { code, state } = parseCallbackQuery(query);
|
|
101
|
+
const stored = await consumeOAuthState(auth.store, state);
|
|
102
|
+
if (typeof stored.codeVerifier !== "string") {
|
|
103
|
+
throw new InvalidOAuthCallbackError("Missing PKCE code verifier for OAuth state");
|
|
104
|
+
}
|
|
105
|
+
const tokens = await this.validateAuthorizationCode(code, stored.codeVerifier);
|
|
106
|
+
let claims;
|
|
107
|
+
if ("id_token" in tokens.data) {
|
|
108
|
+
claims = decodeIdToken(tokens.idToken());
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
claims = await fetchUserProfile(userinfoEndpoint, tokens.accessToken());
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
id: profileId(claims.sub),
|
|
115
|
+
name: profileString(claims.name),
|
|
116
|
+
email: profileString(claims.email) ?? profileString(claims.preferred_username),
|
|
117
|
+
image: profileString(claims.picture)
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { OAuth2Tokens } from "../oauth2.js";
|
|
2
|
+
import type { OAuthCallbackQuery, OAuthUser, ProviderOptions } from "../auth.js";
|
|
3
|
+
export interface MyAnimeListOptions extends ProviderOptions {
|
|
4
|
+
}
|
|
5
|
+
export declare class MyAnimeList {
|
|
6
|
+
private client;
|
|
7
|
+
private auth;
|
|
8
|
+
constructor(options: MyAnimeListOptions);
|
|
9
|
+
constructor(clientId: string, clientSecret: string, redirectURI: string | null);
|
|
10
|
+
createAuthorizationURL(state: string, codeVerifier: string): URL;
|
|
11
|
+
validateAuthorizationCode(code: string, codeVerifier: string): Promise<OAuth2Tokens>;
|
|
12
|
+
refreshAccessToken(refreshToken: string): Promise<OAuth2Tokens>;
|
|
13
|
+
getAuthorizationURL(_scopes?: string[]): Promise<URL>;
|
|
14
|
+
getUser(query: OAuthCallbackQuery): Promise<OAuthUser>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { OAuth2Client, CodeChallengeMethod } from "../client.js";
|
|
2
|
+
import { consumeOAuthState, fetchUserProfile, generateOAuthCodeVerifier, generateOAuthState, InvalidOAuthCallbackError, parseCallbackQuery, profileId, profileString, requireAuthConfig, resolveAuthConfig, saveOAuthState } from "../auth.js";
|
|
3
|
+
const authorizationEndpoint = "https://myanimelist.net/v1/oauth2/authorize";
|
|
4
|
+
const tokenEndpoint = "https://myanimelist.net/v1/oauth2/token";
|
|
5
|
+
const userEndpoint = "https://api.myanimelist.net/v2/users/@me";
|
|
6
|
+
const envPrefix = "MY_ANIME_LIST";
|
|
7
|
+
export class MyAnimeList {
|
|
8
|
+
client;
|
|
9
|
+
auth = null;
|
|
10
|
+
constructor(clientIdOrOptions, clientSecret, redirectURI) {
|
|
11
|
+
if (typeof clientIdOrOptions === "object") {
|
|
12
|
+
this.auth = resolveAuthConfig(envPrefix, clientIdOrOptions, { clientSecret: true });
|
|
13
|
+
this.client = new OAuth2Client(this.auth.clientId, this.auth.clientSecret, this.auth.redirectURI);
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
this.client = new OAuth2Client(clientIdOrOptions, clientSecret ?? null, redirectURI ?? null);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
createAuthorizationURL(state, codeVerifier) {
|
|
20
|
+
const url = this.client.createAuthorizationURLWithPKCE(authorizationEndpoint, state, CodeChallengeMethod.Plain, codeVerifier, []);
|
|
21
|
+
return url;
|
|
22
|
+
}
|
|
23
|
+
async validateAuthorizationCode(code, codeVerifier) {
|
|
24
|
+
const tokens = await this.client.validateAuthorizationCode(tokenEndpoint, code, codeVerifier);
|
|
25
|
+
return tokens;
|
|
26
|
+
}
|
|
27
|
+
async refreshAccessToken(refreshToken) {
|
|
28
|
+
const tokens = await this.client.refreshAccessToken(tokenEndpoint, refreshToken, []);
|
|
29
|
+
return tokens;
|
|
30
|
+
}
|
|
31
|
+
// MyAnimeList does not use scopes, so the argument is ignored.
|
|
32
|
+
async getAuthorizationURL(_scopes) {
|
|
33
|
+
const auth = requireAuthConfig(this.auth);
|
|
34
|
+
const state = generateOAuthState();
|
|
35
|
+
const codeVerifier = generateOAuthCodeVerifier();
|
|
36
|
+
const url = this.createAuthorizationURL(state, codeVerifier);
|
|
37
|
+
await saveOAuthState(auth.store, state, { codeVerifier });
|
|
38
|
+
return url;
|
|
39
|
+
}
|
|
40
|
+
async getUser(query) {
|
|
41
|
+
const auth = requireAuthConfig(this.auth);
|
|
42
|
+
const { code, state } = parseCallbackQuery(query);
|
|
43
|
+
const stored = await consumeOAuthState(auth.store, state);
|
|
44
|
+
if (typeof stored.codeVerifier !== "string") {
|
|
45
|
+
throw new InvalidOAuthCallbackError("Missing PKCE code verifier for OAuth state");
|
|
46
|
+
}
|
|
47
|
+
const tokens = await this.validateAuthorizationCode(code, stored.codeVerifier);
|
|
48
|
+
const profile = await fetchUserProfile(userEndpoint, tokens.accessToken());
|
|
49
|
+
return {
|
|
50
|
+
id: profileId(profile.id),
|
|
51
|
+
name: profileString(profile.name),
|
|
52
|
+
email: null,
|
|
53
|
+
image: profileString(profile.picture)
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { OAuth2Tokens } from "../oauth2.js";
|
|
2
|
+
import type { OAuthCallbackQuery, OAuthUser, ProviderOptions } from "../auth.js";
|
|
3
|
+
export interface NaverOptions extends ProviderOptions {
|
|
4
|
+
}
|
|
5
|
+
export declare class Naver {
|
|
6
|
+
private clientId;
|
|
7
|
+
private clientSecret;
|
|
8
|
+
private redirectURI;
|
|
9
|
+
private auth;
|
|
10
|
+
constructor(options: NaverOptions);
|
|
11
|
+
constructor(clientId: string, clientSecret: string, redirectURI: string);
|
|
12
|
+
createAuthorizationURL(): URL;
|
|
13
|
+
validateAuthorizationCode(code: string): Promise<OAuth2Tokens>;
|
|
14
|
+
refreshAccessToken(refreshToken: string): Promise<OAuth2Tokens>;
|
|
15
|
+
getAuthorizationURL(_scopes?: string[]): Promise<URL>;
|
|
16
|
+
getUser(query: OAuthCallbackQuery): Promise<OAuthUser>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { createOAuth2Request, sendTokenRequest } from "../request.js";
|
|
2
|
+
import { consumeOAuthState, fetchUserProfile, generateOAuthState, OAuthProviderError, parseCallbackQuery, profileId, profileString, requireAuthConfig, resolveAuthConfig, saveOAuthState } from "../auth.js";
|
|
3
|
+
const authorizationEndpoint = "https://nid.naver.com/oauth2.0/authorize";
|
|
4
|
+
const tokenEndpoint = "https://nid.naver.com/oauth2.0/token";
|
|
5
|
+
const userEndpoint = "https://openapi.naver.com/v1/nid/me";
|
|
6
|
+
const envPrefix = "NAVER";
|
|
7
|
+
export class Naver {
|
|
8
|
+
clientId;
|
|
9
|
+
clientSecret;
|
|
10
|
+
redirectURI;
|
|
11
|
+
auth = null;
|
|
12
|
+
constructor(clientIdOrOptions, clientSecret, redirectURI) {
|
|
13
|
+
if (typeof clientIdOrOptions === "object") {
|
|
14
|
+
this.auth = resolveAuthConfig(envPrefix, clientIdOrOptions, {
|
|
15
|
+
clientSecret: true,
|
|
16
|
+
redirectURI: true
|
|
17
|
+
});
|
|
18
|
+
this.clientId = this.auth.clientId;
|
|
19
|
+
this.clientSecret = this.auth.clientSecret ?? "";
|
|
20
|
+
this.redirectURI = this.auth.redirectURI ?? "";
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
this.clientId = clientIdOrOptions;
|
|
24
|
+
this.clientSecret = clientSecret ?? "";
|
|
25
|
+
this.redirectURI = redirectURI ?? "";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
createAuthorizationURL() {
|
|
29
|
+
const url = new URL(authorizationEndpoint);
|
|
30
|
+
url.searchParams.set("response_type", "code");
|
|
31
|
+
url.searchParams.set("client_id", this.clientId);
|
|
32
|
+
url.searchParams.set("redirect_uri", this.redirectURI);
|
|
33
|
+
return url;
|
|
34
|
+
}
|
|
35
|
+
async validateAuthorizationCode(code) {
|
|
36
|
+
const body = new URLSearchParams();
|
|
37
|
+
body.set("grant_type", "authorization_code");
|
|
38
|
+
body.set("client_id", this.clientId);
|
|
39
|
+
body.set("client_secret", this.clientSecret);
|
|
40
|
+
body.set("code", code);
|
|
41
|
+
body.set("redirect_uri", this.redirectURI);
|
|
42
|
+
const request = createOAuth2Request(tokenEndpoint, body);
|
|
43
|
+
const tokens = await sendTokenRequest(request);
|
|
44
|
+
return tokens;
|
|
45
|
+
}
|
|
46
|
+
async refreshAccessToken(refreshToken) {
|
|
47
|
+
const body = new URLSearchParams();
|
|
48
|
+
body.set("grant_type", "refresh_token");
|
|
49
|
+
body.set("client_id", this.clientId);
|
|
50
|
+
body.set("client_secret", this.clientSecret);
|
|
51
|
+
body.set("refresh_token", refreshToken);
|
|
52
|
+
const request = createOAuth2Request(tokenEndpoint, body);
|
|
53
|
+
const tokens = await sendTokenRequest(request);
|
|
54
|
+
return tokens;
|
|
55
|
+
}
|
|
56
|
+
// Naver scopes are configured in the application settings, so the argument is ignored.
|
|
57
|
+
async getAuthorizationURL(_scopes) {
|
|
58
|
+
const auth = requireAuthConfig(this.auth);
|
|
59
|
+
const state = generateOAuthState();
|
|
60
|
+
const url = this.createAuthorizationURL();
|
|
61
|
+
url.searchParams.set("state", state);
|
|
62
|
+
await saveOAuthState(auth.store, state, {});
|
|
63
|
+
return url;
|
|
64
|
+
}
|
|
65
|
+
async getUser(query) {
|
|
66
|
+
const auth = requireAuthConfig(this.auth);
|
|
67
|
+
const { code, state } = parseCallbackQuery(query);
|
|
68
|
+
await consumeOAuthState(auth.store, state);
|
|
69
|
+
const tokens = await this.validateAuthorizationCode(code);
|
|
70
|
+
const profile = await fetchUserProfile(userEndpoint, tokens.accessToken());
|
|
71
|
+
const response = profile.response;
|
|
72
|
+
if (typeof response !== "object" || response === null) {
|
|
73
|
+
throw new OAuthProviderError("Unexpected user profile response body");
|
|
74
|
+
}
|
|
75
|
+
const user = response;
|
|
76
|
+
return {
|
|
77
|
+
id: profileId(user.id),
|
|
78
|
+
name: profileString(user.name) ?? profileString(user.nickname),
|
|
79
|
+
email: profileString(user.email),
|
|
80
|
+
image: profileString(user.profile_image)
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { OAuth2Tokens } from "../oauth2.js";
|
|
2
|
+
import type { OAuthCallbackQuery, OAuthUser, ProviderOptions } from "../auth.js";
|
|
3
|
+
export interface NotionOptions extends ProviderOptions {
|
|
4
|
+
}
|
|
5
|
+
export declare class Notion {
|
|
6
|
+
private client;
|
|
7
|
+
private auth;
|
|
8
|
+
constructor(options: NotionOptions);
|
|
9
|
+
constructor(clientId: string, clientSecret: string, redirectURI: string);
|
|
10
|
+
createAuthorizationURL(state: string): URL;
|
|
11
|
+
validateAuthorizationCode(code: string): Promise<OAuth2Tokens>;
|
|
12
|
+
getAuthorizationURL(_scopes?: string[]): Promise<URL>;
|
|
13
|
+
getUser(query: OAuthCallbackQuery): Promise<OAuthUser>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { OAuth2Client } from "../client.js";
|
|
2
|
+
import { consumeOAuthState, generateOAuthState, OAuthProviderError, parseCallbackQuery, profileId, profileString, requireAuthConfig, resolveAuthConfig, saveOAuthState } from "../auth.js";
|
|
3
|
+
const authorizationEndpoint = "https://api.notion.com/v1/oauth/authorize";
|
|
4
|
+
const tokenEndpoint = "https://api.notion.com/v1/oauth/token";
|
|
5
|
+
const envPrefix = "NOTION";
|
|
6
|
+
export class Notion {
|
|
7
|
+
client;
|
|
8
|
+
auth = null;
|
|
9
|
+
constructor(clientIdOrOptions, clientSecret, redirectURI) {
|
|
10
|
+
if (typeof clientIdOrOptions === "object") {
|
|
11
|
+
this.auth = resolveAuthConfig(envPrefix, clientIdOrOptions, {
|
|
12
|
+
clientSecret: true,
|
|
13
|
+
redirectURI: true
|
|
14
|
+
});
|
|
15
|
+
this.client = new OAuth2Client(this.auth.clientId, this.auth.clientSecret, this.auth.redirectURI);
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
this.client = new OAuth2Client(clientIdOrOptions, clientSecret ?? null, redirectURI ?? null);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
createAuthorizationURL(state) {
|
|
22
|
+
const url = this.client.createAuthorizationURL(authorizationEndpoint, state, []);
|
|
23
|
+
url.searchParams.set("owner", "user");
|
|
24
|
+
return url;
|
|
25
|
+
}
|
|
26
|
+
async validateAuthorizationCode(code) {
|
|
27
|
+
const tokens = await this.client.validateAuthorizationCode(tokenEndpoint, code, null);
|
|
28
|
+
return tokens;
|
|
29
|
+
}
|
|
30
|
+
// Notion does not use scopes, so the argument is ignored.
|
|
31
|
+
async getAuthorizationURL(_scopes) {
|
|
32
|
+
const auth = requireAuthConfig(this.auth);
|
|
33
|
+
const state = generateOAuthState();
|
|
34
|
+
const url = this.createAuthorizationURL(state);
|
|
35
|
+
await saveOAuthState(auth.store, state, {});
|
|
36
|
+
return url;
|
|
37
|
+
}
|
|
38
|
+
async getUser(query) {
|
|
39
|
+
const auth = requireAuthConfig(this.auth);
|
|
40
|
+
const { code, state } = parseCallbackQuery(query);
|
|
41
|
+
await consumeOAuthState(auth.store, state);
|
|
42
|
+
const tokens = await this.validateAuthorizationCode(code);
|
|
43
|
+
// Notion returns the authorizing user inside the token response.
|
|
44
|
+
const data = tokens.data;
|
|
45
|
+
const owner = data.owner;
|
|
46
|
+
if (typeof owner !== "object" || owner === null) {
|
|
47
|
+
throw new OAuthProviderError("Notion token response is missing the owner");
|
|
48
|
+
}
|
|
49
|
+
const user = owner.user;
|
|
50
|
+
if (typeof user !== "object" || user === null) {
|
|
51
|
+
throw new OAuthProviderError("Notion token response is missing the user");
|
|
52
|
+
}
|
|
53
|
+
const profile = user;
|
|
54
|
+
let email = null;
|
|
55
|
+
if (typeof profile.person === "object" && profile.person !== null) {
|
|
56
|
+
email = profileString(profile.person.email);
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
id: profileId(profile.id),
|
|
60
|
+
name: profileString(profile.name),
|
|
61
|
+
email,
|
|
62
|
+
image: profileString(profile.avatar_url)
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { OAuth2Tokens } from "../oauth2.js";
|
|
2
|
+
import type { OAuthCallbackQuery, OAuthUser, ProviderOptions } from "../auth.js";
|
|
3
|
+
export interface OktaOptions extends ProviderOptions {
|
|
4
|
+
domain?: string;
|
|
5
|
+
authorizationServerId?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare class Okta {
|
|
8
|
+
private authorizationEndpoint;
|
|
9
|
+
private tokenEndpoint;
|
|
10
|
+
private tokenRevocationEndpoint;
|
|
11
|
+
private userinfoEndpoint;
|
|
12
|
+
private client;
|
|
13
|
+
private auth;
|
|
14
|
+
constructor(options: OktaOptions);
|
|
15
|
+
constructor(domain: string, authorizationServerId: string | null, clientId: string, clientSecret: string, redirectURI: string);
|
|
16
|
+
createAuthorizationURL(state: string, codeVerifier: string, scopes: string[]): URL;
|
|
17
|
+
validateAuthorizationCode(code: string, codeVerifier: string): Promise<OAuth2Tokens>;
|
|
18
|
+
refreshAccessToken(refreshToken: string, scopes: string[]): Promise<OAuth2Tokens>;
|
|
19
|
+
revokeToken(token: string): Promise<void>;
|
|
20
|
+
getAuthorizationURL(scopes?: string[]): Promise<URL>;
|
|
21
|
+
getUser(query: OAuthCallbackQuery): Promise<OAuthUser>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { CodeChallengeMethod, OAuth2Client } from "../client.js";
|
|
2
|
+
import { decodeIdToken } from "../oidc.js";
|
|
3
|
+
import { consumeOAuthState, fetchUserProfile, generateOAuthCodeVerifier, generateOAuthState, InvalidOAuthCallbackError, parseCallbackQuery, profileId, profileString, requireAuthConfig, requireProviderOption, resolveAuthConfig, resolveProviderOption, resolveScopes, saveOAuthState } from "../auth.js";
|
|
4
|
+
import { joinURIAndPath } from "../request.js";
|
|
5
|
+
const envPrefix = "OKTA";
|
|
6
|
+
const defaultScopes = ["openid", "profile", "email"];
|
|
7
|
+
export class Okta {
|
|
8
|
+
authorizationEndpoint;
|
|
9
|
+
tokenEndpoint;
|
|
10
|
+
tokenRevocationEndpoint;
|
|
11
|
+
userinfoEndpoint;
|
|
12
|
+
client;
|
|
13
|
+
auth = null;
|
|
14
|
+
constructor(domainOrOptions, authorizationServerId, clientId, clientSecret, redirectURI) {
|
|
15
|
+
let domain;
|
|
16
|
+
let serverId;
|
|
17
|
+
if (typeof domainOrOptions === "object") {
|
|
18
|
+
this.auth = resolveAuthConfig(envPrefix, domainOrOptions, {
|
|
19
|
+
clientSecret: true,
|
|
20
|
+
redirectURI: true
|
|
21
|
+
});
|
|
22
|
+
domain = requireProviderOption(domainOrOptions.domain, envPrefix, "DOMAIN", "domain");
|
|
23
|
+
serverId = resolveProviderOption(domainOrOptions.authorizationServerId, envPrefix, "AUTHORIZATION_SERVER_ID");
|
|
24
|
+
this.client = new OAuth2Client(this.auth.clientId, this.auth.clientSecret, this.auth.redirectURI);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
domain = domainOrOptions;
|
|
28
|
+
serverId = authorizationServerId ?? null;
|
|
29
|
+
this.client = new OAuth2Client(clientId ?? "", clientSecret ?? null, redirectURI ?? null);
|
|
30
|
+
}
|
|
31
|
+
let baseURL = `https://${domain}/oauth2`;
|
|
32
|
+
if (serverId !== null) {
|
|
33
|
+
baseURL = joinURIAndPath(baseURL, serverId);
|
|
34
|
+
}
|
|
35
|
+
this.authorizationEndpoint = joinURIAndPath(baseURL, "/v1/authorize");
|
|
36
|
+
this.tokenEndpoint = joinURIAndPath(baseURL, "/v1/token");
|
|
37
|
+
this.tokenRevocationEndpoint = joinURIAndPath(baseURL, "/v1/revoke");
|
|
38
|
+
this.userinfoEndpoint = joinURIAndPath(baseURL, "/v1/userinfo");
|
|
39
|
+
}
|
|
40
|
+
createAuthorizationURL(state, codeVerifier, scopes) {
|
|
41
|
+
const url = this.client.createAuthorizationURLWithPKCE(this.authorizationEndpoint, state, CodeChallengeMethod.S256, codeVerifier, scopes);
|
|
42
|
+
return url;
|
|
43
|
+
}
|
|
44
|
+
async validateAuthorizationCode(code, codeVerifier) {
|
|
45
|
+
const tokens = await this.client.validateAuthorizationCode(this.tokenEndpoint, code, codeVerifier);
|
|
46
|
+
return tokens;
|
|
47
|
+
}
|
|
48
|
+
async refreshAccessToken(refreshToken, scopes) {
|
|
49
|
+
const tokens = await this.client.refreshAccessToken(this.tokenEndpoint, refreshToken, scopes);
|
|
50
|
+
return tokens;
|
|
51
|
+
}
|
|
52
|
+
async revokeToken(token) {
|
|
53
|
+
await this.client.revokeToken(this.tokenRevocationEndpoint, token);
|
|
54
|
+
}
|
|
55
|
+
async getAuthorizationURL(scopes) {
|
|
56
|
+
const auth = requireAuthConfig(this.auth);
|
|
57
|
+
const state = generateOAuthState();
|
|
58
|
+
const codeVerifier = generateOAuthCodeVerifier();
|
|
59
|
+
const url = this.createAuthorizationURL(state, codeVerifier, resolveScopes(scopes, auth, defaultScopes));
|
|
60
|
+
await saveOAuthState(auth.store, state, { codeVerifier });
|
|
61
|
+
return url;
|
|
62
|
+
}
|
|
63
|
+
async getUser(query) {
|
|
64
|
+
const auth = requireAuthConfig(this.auth);
|
|
65
|
+
const { code, state } = parseCallbackQuery(query);
|
|
66
|
+
const stored = await consumeOAuthState(auth.store, state);
|
|
67
|
+
if (typeof stored.codeVerifier !== "string") {
|
|
68
|
+
throw new InvalidOAuthCallbackError("Missing PKCE code verifier for OAuth state");
|
|
69
|
+
}
|
|
70
|
+
const tokens = await this.validateAuthorizationCode(code, stored.codeVerifier);
|
|
71
|
+
let claims;
|
|
72
|
+
if ("id_token" in tokens.data) {
|
|
73
|
+
claims = decodeIdToken(tokens.idToken());
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
claims = await fetchUserProfile(this.userinfoEndpoint, tokens.accessToken());
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
id: profileId(claims.sub),
|
|
80
|
+
name: profileString(claims.name),
|
|
81
|
+
email: profileString(claims.email),
|
|
82
|
+
image: profileString(claims.picture)
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { OAuth2Tokens } from "../oauth2.js";
|
|
2
|
+
import type { OAuthCallbackQuery, OAuthUser, ProviderOptions } from "../auth.js";
|
|
3
|
+
export interface OsuOptions extends ProviderOptions {
|
|
4
|
+
}
|
|
5
|
+
export declare class Osu {
|
|
6
|
+
private clientId;
|
|
7
|
+
private clientSecret;
|
|
8
|
+
private redirectURI;
|
|
9
|
+
private auth;
|
|
10
|
+
constructor(options: OsuOptions);
|
|
11
|
+
constructor(clientId: string, clientSecret: string, redirectURI: string | null);
|
|
12
|
+
createAuthorizationURL(state: string, scopes: string[]): URL;
|
|
13
|
+
validateAuthorizationCode(code: string): Promise<OAuth2Tokens>;
|
|
14
|
+
refreshAccessToken(refreshToken: string): Promise<OAuth2Tokens>;
|
|
15
|
+
getAuthorizationURL(scopes?: string[]): Promise<URL>;
|
|
16
|
+
getUser(query: OAuthCallbackQuery): Promise<OAuthUser>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { createOAuth2Request, sendTokenRequest } from "../request.js";
|
|
2
|
+
import { consumeOAuthState, fetchUserProfile, generateOAuthState, parseCallbackQuery, profileId, profileString, requireAuthConfig, resolveAuthConfig, resolveScopes, saveOAuthState } from "../auth.js";
|
|
3
|
+
const authorizationEndpoint = "https://osu.ppy.sh/oauth/authorize";
|
|
4
|
+
const tokenEndpoint = "https://osu.ppy.sh/oauth/token";
|
|
5
|
+
const userEndpoint = "https://osu.ppy.sh/api/v2/me";
|
|
6
|
+
const envPrefix = "OSU";
|
|
7
|
+
const defaultScopes = ["identify"];
|
|
8
|
+
export class Osu {
|
|
9
|
+
clientId;
|
|
10
|
+
clientSecret;
|
|
11
|
+
redirectURI;
|
|
12
|
+
auth = null;
|
|
13
|
+
constructor(clientIdOrOptions, clientSecret, redirectURI) {
|
|
14
|
+
if (typeof clientIdOrOptions === "object") {
|
|
15
|
+
this.auth = resolveAuthConfig(envPrefix, clientIdOrOptions, { clientSecret: true });
|
|
16
|
+
this.clientId = this.auth.clientId;
|
|
17
|
+
this.clientSecret = this.auth.clientSecret ?? "";
|
|
18
|
+
this.redirectURI = this.auth.redirectURI;
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
this.clientId = clientIdOrOptions;
|
|
22
|
+
this.clientSecret = clientSecret ?? "";
|
|
23
|
+
this.redirectURI = redirectURI ?? null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
createAuthorizationURL(state, scopes) {
|
|
27
|
+
const url = new URL(authorizationEndpoint);
|
|
28
|
+
url.searchParams.set("response_type", "code");
|
|
29
|
+
url.searchParams.set("client_id", this.clientId);
|
|
30
|
+
url.searchParams.set("state", state);
|
|
31
|
+
if (scopes.length > 0) {
|
|
32
|
+
url.searchParams.set("scope", scopes.join(" "));
|
|
33
|
+
}
|
|
34
|
+
if (this.redirectURI !== null) {
|
|
35
|
+
url.searchParams.set("redirect_uri", this.redirectURI);
|
|
36
|
+
}
|
|
37
|
+
return url;
|
|
38
|
+
}
|
|
39
|
+
async validateAuthorizationCode(code) {
|
|
40
|
+
const body = new URLSearchParams();
|
|
41
|
+
body.set("grant_type", "authorization_code");
|
|
42
|
+
body.set("code", code);
|
|
43
|
+
if (this.redirectURI !== null) {
|
|
44
|
+
body.set("redirect_uri", this.redirectURI);
|
|
45
|
+
}
|
|
46
|
+
body.set("client_id", this.clientId);
|
|
47
|
+
body.set("client_secret", this.clientSecret);
|
|
48
|
+
const request = createOAuth2Request(tokenEndpoint, body);
|
|
49
|
+
const tokens = await sendTokenRequest(request);
|
|
50
|
+
return tokens;
|
|
51
|
+
}
|
|
52
|
+
async refreshAccessToken(refreshToken) {
|
|
53
|
+
const body = new URLSearchParams();
|
|
54
|
+
body.set("grant_type", "refresh_token");
|
|
55
|
+
body.set("refresh_token", refreshToken);
|
|
56
|
+
body.set("client_id", this.clientId);
|
|
57
|
+
body.set("client_secret", this.clientSecret);
|
|
58
|
+
const request = createOAuth2Request(tokenEndpoint, body);
|
|
59
|
+
const tokens = await sendTokenRequest(request);
|
|
60
|
+
return tokens;
|
|
61
|
+
}
|
|
62
|
+
async getAuthorizationURL(scopes) {
|
|
63
|
+
const auth = requireAuthConfig(this.auth);
|
|
64
|
+
const state = generateOAuthState();
|
|
65
|
+
const url = this.createAuthorizationURL(state, resolveScopes(scopes, auth, defaultScopes));
|
|
66
|
+
await saveOAuthState(auth.store, state, {});
|
|
67
|
+
return url;
|
|
68
|
+
}
|
|
69
|
+
async getUser(query) {
|
|
70
|
+
const auth = requireAuthConfig(this.auth);
|
|
71
|
+
const { code, state } = parseCallbackQuery(query);
|
|
72
|
+
await consumeOAuthState(auth.store, state);
|
|
73
|
+
const tokens = await this.validateAuthorizationCode(code);
|
|
74
|
+
const profile = await fetchUserProfile(userEndpoint, tokens.accessToken());
|
|
75
|
+
return {
|
|
76
|
+
id: profileId(profile.id),
|
|
77
|
+
name: profileString(profile.username),
|
|
78
|
+
email: null,
|
|
79
|
+
image: profileString(profile.avatar_url)
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { OAuth2Tokens } from "../oauth2.js";
|
|
2
|
+
import type { OAuthCallbackQuery, OAuthUser, ProviderOptions } from "../auth.js";
|
|
3
|
+
export interface PatreonOptions extends ProviderOptions {
|
|
4
|
+
}
|
|
5
|
+
export declare class Patreon {
|
|
6
|
+
private clientId;
|
|
7
|
+
private clientSecret;
|
|
8
|
+
private redirectURI;
|
|
9
|
+
private auth;
|
|
10
|
+
constructor(options: PatreonOptions);
|
|
11
|
+
constructor(clientId: string, clientSecret: string, redirectURI: string);
|
|
12
|
+
createAuthorizationURL(state: string, scopes: string[]): URL;
|
|
13
|
+
validateAuthorizationCode(code: string): Promise<OAuth2Tokens>;
|
|
14
|
+
refreshAccessToken(refreshToken: string): Promise<OAuth2Tokens>;
|
|
15
|
+
getAuthorizationURL(scopes?: string[]): Promise<URL>;
|
|
16
|
+
getUser(query: OAuthCallbackQuery): Promise<OAuthUser>;
|
|
17
|
+
}
|