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
package/dist/index.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
export { AmazonCognito } from "./providers/amazon-cognito.js";
|
|
2
|
+
export { AniList } from "./providers/anilist.js";
|
|
3
|
+
export { Apple } from "./providers/apple.js";
|
|
4
|
+
export { Atlassian } from "./providers/atlassian.js";
|
|
5
|
+
export { Auth0 } from "./providers/auth0.js";
|
|
6
|
+
export { Authentik } from "./providers/authentik.js";
|
|
7
|
+
export { Autodesk } from "./providers/autodesk.js";
|
|
8
|
+
export { BattleNet } from "./providers/battlenet.js";
|
|
9
|
+
export { Bitbucket } from "./providers/bitbucket.js";
|
|
10
|
+
export { Box } from "./providers/box.js";
|
|
11
|
+
export { Bungie } from "./providers/bungie.js";
|
|
12
|
+
export { Coinbase } from "./providers/coinbase.js";
|
|
13
|
+
export { Discord } from "./providers/discord.js";
|
|
14
|
+
export { DonationAlerts } from "./providers/donation-alerts.js";
|
|
15
|
+
export { Dribbble } from "./providers/dribbble.js";
|
|
16
|
+
export { Dropbox } from "./providers/dropbox.js";
|
|
17
|
+
export { Etsy } from "./providers/etsy.js";
|
|
18
|
+
export { EpicGames } from "./providers/epicgames.js";
|
|
19
|
+
export { Facebook } from "./providers/facebook.js";
|
|
20
|
+
export { Figma } from "./providers/figma.js";
|
|
21
|
+
export { Gitea } from "./providers/gitea.js";
|
|
22
|
+
export { GitHub } from "./providers/github.js";
|
|
23
|
+
export { GitLab } from "./providers/gitlab.js";
|
|
24
|
+
export { Google } from "./providers/google.js";
|
|
25
|
+
export { Intuit } from "./providers/intuit.js";
|
|
26
|
+
export { Kakao } from "./providers/kakao.js";
|
|
27
|
+
export { Kick } from "./providers/kick.js";
|
|
28
|
+
export { KeyCloak } from "./providers/keycloak.js";
|
|
29
|
+
export { Lichess } from "./providers/lichess.js";
|
|
30
|
+
export { Line } from "./providers/line.js";
|
|
31
|
+
export { Linear } from "./providers/linear.js";
|
|
32
|
+
export { LinkedIn } from "./providers/linkedin.js";
|
|
33
|
+
export { Mastodon } from "./providers/mastodon.js";
|
|
34
|
+
export { MercadoLibre } from "./providers/mercadolibre.js";
|
|
35
|
+
export { MercadoPago } from "./providers/mercadopago.js";
|
|
36
|
+
export { MicrosoftEntraId } from "./providers/microsoft-entra-id.js";
|
|
37
|
+
export { MyAnimeList } from "./providers/myanimelist.js";
|
|
38
|
+
export { Naver } from "./providers/naver.js";
|
|
39
|
+
export { Notion } from "./providers/notion.js";
|
|
40
|
+
export { Okta } from "./providers/okta.js";
|
|
41
|
+
export { Osu } from "./providers/osu.js";
|
|
42
|
+
export { Patreon } from "./providers/patreon.js";
|
|
43
|
+
export { Polar } from "./providers/polar.js";
|
|
44
|
+
export { Reddit } from "./providers/reddit.js";
|
|
45
|
+
export { Roblox } from "./providers/roblox.js";
|
|
46
|
+
export { Salesforce } from "./providers/salesforce.js";
|
|
47
|
+
export { Shikimori } from "./providers/shikimori.js";
|
|
48
|
+
export { Slack } from "./providers/slack.js";
|
|
49
|
+
export { Spotify } from "./providers/spotify.js";
|
|
50
|
+
export { StartGG } from "./providers/startgg.js";
|
|
51
|
+
export { Strava } from "./providers/strava.js";
|
|
52
|
+
export { Synology } from "./providers/synology.js";
|
|
53
|
+
export { TikTok } from "./providers/tiktok.js";
|
|
54
|
+
export { Tiltify } from "./providers/tiltify.js";
|
|
55
|
+
export { Tumblr } from "./providers/tumblr.js";
|
|
56
|
+
export { Twitch } from "./providers/twitch.js";
|
|
57
|
+
export { Twitter } from "./providers/twitter.js";
|
|
58
|
+
export { VK } from "./providers/vk.js";
|
|
59
|
+
export { Withings } from "./providers/withings.js";
|
|
60
|
+
export { WorkOS } from "./providers/workos.js";
|
|
61
|
+
export { Yahoo } from "./providers/yahoo.js";
|
|
62
|
+
export { Yandex } from "./providers/yandex.js";
|
|
63
|
+
export { Zoom } from "./providers/zoom.js";
|
|
64
|
+
export { FortyTwo } from "./providers/42.js";
|
|
65
|
+
export { OAuth2Client, CodeChallengeMethod } from "./client.js";
|
|
66
|
+
export { OAuth2Tokens, generateCodeVerifier, generateState } from "./oauth2.js";
|
|
67
|
+
export { OAuth2RequestError, ArcticFetchError, UnexpectedErrorResponseBodyError, UnexpectedResponseError } from "./request.js";
|
|
68
|
+
export { decodeIdToken } from "./oidc.js";
|
|
69
|
+
export { InvalidOAuthCallbackError, InvalidOAuthStateError, OAuthConfigurationError, OAuthProviderError } from "./auth.js";
|
package/dist/oauth2.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare class OAuth2Tokens {
|
|
2
|
+
data: object;
|
|
3
|
+
constructor(data: object);
|
|
4
|
+
tokenType(): string;
|
|
5
|
+
accessToken(): string;
|
|
6
|
+
accessTokenExpiresInSeconds(): number;
|
|
7
|
+
accessTokenExpiresAt(): Date;
|
|
8
|
+
hasRefreshToken(): boolean;
|
|
9
|
+
refreshToken(): string;
|
|
10
|
+
hasScopes(): boolean;
|
|
11
|
+
scopes(): string[];
|
|
12
|
+
idToken(): string;
|
|
13
|
+
}
|
|
14
|
+
export declare function createS256CodeChallenge(codeVerifier: string): string;
|
|
15
|
+
export declare function generateCodeVerifier(): string;
|
|
16
|
+
export declare function generateState(): string;
|
package/dist/oauth2.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import * as encoding from "@oslojs/encoding";
|
|
2
|
+
import * as sha2 from "@oslojs/crypto/sha2";
|
|
3
|
+
export class OAuth2Tokens {
|
|
4
|
+
data;
|
|
5
|
+
constructor(data) {
|
|
6
|
+
this.data = data;
|
|
7
|
+
}
|
|
8
|
+
tokenType() {
|
|
9
|
+
if ("token_type" in this.data && typeof this.data.token_type === "string") {
|
|
10
|
+
return this.data.token_type;
|
|
11
|
+
}
|
|
12
|
+
throw new Error("Missing or invalid 'token_type' field");
|
|
13
|
+
}
|
|
14
|
+
accessToken() {
|
|
15
|
+
if ("access_token" in this.data && typeof this.data.access_token === "string") {
|
|
16
|
+
return this.data.access_token;
|
|
17
|
+
}
|
|
18
|
+
throw new Error("Missing or invalid 'access_token' field");
|
|
19
|
+
}
|
|
20
|
+
accessTokenExpiresInSeconds() {
|
|
21
|
+
if ("expires_in" in this.data && typeof this.data.expires_in === "number") {
|
|
22
|
+
return this.data.expires_in;
|
|
23
|
+
}
|
|
24
|
+
throw new Error("Missing or invalid 'expires_in' field");
|
|
25
|
+
}
|
|
26
|
+
accessTokenExpiresAt() {
|
|
27
|
+
return new Date(Date.now() + this.accessTokenExpiresInSeconds() * 1000);
|
|
28
|
+
}
|
|
29
|
+
hasRefreshToken() {
|
|
30
|
+
return "refresh_token" in this.data && typeof this.data.refresh_token === "string";
|
|
31
|
+
}
|
|
32
|
+
refreshToken() {
|
|
33
|
+
if ("refresh_token" in this.data && typeof this.data.refresh_token === "string") {
|
|
34
|
+
return this.data.refresh_token;
|
|
35
|
+
}
|
|
36
|
+
throw new Error("Missing or invalid 'refresh_token' field");
|
|
37
|
+
}
|
|
38
|
+
hasScopes() {
|
|
39
|
+
return "scope" in this.data && typeof this.data.scope === "string";
|
|
40
|
+
}
|
|
41
|
+
scopes() {
|
|
42
|
+
if ("scope" in this.data && typeof this.data.scope === "string") {
|
|
43
|
+
return this.data.scope.split(" ");
|
|
44
|
+
}
|
|
45
|
+
throw new Error("Missing or invalid 'scope' field");
|
|
46
|
+
}
|
|
47
|
+
idToken() {
|
|
48
|
+
if ("id_token" in this.data && typeof this.data.id_token === "string") {
|
|
49
|
+
return this.data.id_token;
|
|
50
|
+
}
|
|
51
|
+
throw new Error("Missing or invalid field 'id_token'");
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
export function createS256CodeChallenge(codeVerifier) {
|
|
55
|
+
const codeChallengeBytes = sha2.sha256(new TextEncoder().encode(codeVerifier));
|
|
56
|
+
return encoding.encodeBase64urlNoPadding(codeChallengeBytes);
|
|
57
|
+
}
|
|
58
|
+
export function generateCodeVerifier() {
|
|
59
|
+
const randomValues = new Uint8Array(32);
|
|
60
|
+
crypto.getRandomValues(randomValues);
|
|
61
|
+
return encoding.encodeBase64urlNoPadding(randomValues);
|
|
62
|
+
}
|
|
63
|
+
export function generateState() {
|
|
64
|
+
const randomValues = new Uint8Array(32);
|
|
65
|
+
crypto.getRandomValues(randomValues);
|
|
66
|
+
return encoding.encodeBase64urlNoPadding(randomValues);
|
|
67
|
+
}
|
package/dist/oidc.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function decodeIdToken(idToken: string): object;
|
package/dist/oidc.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { OAuth2Tokens } from "../oauth2.js";
|
|
2
|
+
import type { OAuthCallbackQuery, OAuthUser, ProviderOptions } from "../auth.js";
|
|
3
|
+
export interface FortyTwoOptions extends ProviderOptions {
|
|
4
|
+
}
|
|
5
|
+
export declare class FortyTwo {
|
|
6
|
+
private clientId;
|
|
7
|
+
private clientSecret;
|
|
8
|
+
private redirectURI;
|
|
9
|
+
private auth;
|
|
10
|
+
constructor(options: FortyTwoOptions);
|
|
11
|
+
constructor(clientId: string, clientSecret: string, redirectURI: string);
|
|
12
|
+
createAuthorizationURL(state: string, scopes: string[]): URL;
|
|
13
|
+
validateAuthorizationCode(code: string): Promise<OAuth2Tokens>;
|
|
14
|
+
getAuthorizationURL(scopes?: string[]): Promise<URL>;
|
|
15
|
+
getUser(query: OAuthCallbackQuery): Promise<OAuthUser>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
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://api.intra.42.fr/oauth/authorize";
|
|
4
|
+
const tokenEndpoint = "https://api.intra.42.fr/oauth/token";
|
|
5
|
+
const userEndpoint = "https://api.intra.42.fr/v2/me";
|
|
6
|
+
const envPrefix = "FORTY_TWO";
|
|
7
|
+
const defaultScopes = ["public"];
|
|
8
|
+
export class FortyTwo {
|
|
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, {
|
|
16
|
+
clientSecret: true,
|
|
17
|
+
redirectURI: true
|
|
18
|
+
});
|
|
19
|
+
this.clientId = this.auth.clientId;
|
|
20
|
+
this.clientSecret = this.auth.clientSecret ?? "";
|
|
21
|
+
this.redirectURI = this.auth.redirectURI ?? "";
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
this.clientId = clientIdOrOptions;
|
|
25
|
+
this.clientSecret = clientSecret ?? "";
|
|
26
|
+
this.redirectURI = redirectURI ?? "";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
createAuthorizationURL(state, scopes) {
|
|
30
|
+
const url = new URL(authorizationEndpoint);
|
|
31
|
+
url.searchParams.set("response_type", "code");
|
|
32
|
+
url.searchParams.set("client_id", this.clientId);
|
|
33
|
+
url.searchParams.set("state", state);
|
|
34
|
+
if (scopes.length > 0) {
|
|
35
|
+
url.searchParams.set("scope", scopes.join(" "));
|
|
36
|
+
}
|
|
37
|
+
url.searchParams.set("redirect_uri", this.redirectURI);
|
|
38
|
+
return url;
|
|
39
|
+
}
|
|
40
|
+
async validateAuthorizationCode(code) {
|
|
41
|
+
const body = new URLSearchParams();
|
|
42
|
+
body.set("grant_type", "authorization_code");
|
|
43
|
+
body.set("code", code);
|
|
44
|
+
body.set("redirect_uri", this.redirectURI);
|
|
45
|
+
body.set("client_id", this.clientId);
|
|
46
|
+
body.set("client_secret", this.clientSecret);
|
|
47
|
+
const request = createOAuth2Request(tokenEndpoint, body);
|
|
48
|
+
const tokens = await sendTokenRequest(request);
|
|
49
|
+
return tokens;
|
|
50
|
+
}
|
|
51
|
+
async getAuthorizationURL(scopes) {
|
|
52
|
+
const auth = requireAuthConfig(this.auth);
|
|
53
|
+
const state = generateOAuthState();
|
|
54
|
+
const url = this.createAuthorizationURL(state, resolveScopes(scopes, auth, defaultScopes));
|
|
55
|
+
await saveOAuthState(auth.store, state, {});
|
|
56
|
+
return url;
|
|
57
|
+
}
|
|
58
|
+
async getUser(query) {
|
|
59
|
+
const auth = requireAuthConfig(this.auth);
|
|
60
|
+
const { code, state } = parseCallbackQuery(query);
|
|
61
|
+
await consumeOAuthState(auth.store, state);
|
|
62
|
+
const tokens = await this.validateAuthorizationCode(code);
|
|
63
|
+
const profile = await fetchUserProfile(userEndpoint, tokens.accessToken());
|
|
64
|
+
let image = null;
|
|
65
|
+
if (typeof profile.image === "object" && profile.image !== null) {
|
|
66
|
+
image = profileString(profile.image.link);
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
id: profileId(profile.id),
|
|
70
|
+
name: profileString(profile.displayname) ?? profileString(profile.login),
|
|
71
|
+
email: profileString(profile.email),
|
|
72
|
+
image
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { OAuth2Tokens } from "../oauth2.js";
|
|
2
|
+
import type { OAuthCallbackQuery, OAuthUser, ProviderOptions } from "../auth.js";
|
|
3
|
+
export interface AmazonCognitoOptions extends ProviderOptions {
|
|
4
|
+
domain?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare class AmazonCognito {
|
|
7
|
+
private authorizationEndpoint;
|
|
8
|
+
private tokenEndpoint;
|
|
9
|
+
private tokenRevocationEndpoint;
|
|
10
|
+
private userinfoEndpoint;
|
|
11
|
+
private client;
|
|
12
|
+
private auth;
|
|
13
|
+
constructor(options: AmazonCognitoOptions);
|
|
14
|
+
constructor(domain: 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
|
+
revokeToken(token: string): Promise<void>;
|
|
19
|
+
getAuthorizationURL(scopes?: string[]): Promise<URL>;
|
|
20
|
+
getUser(query: OAuthCallbackQuery): Promise<OAuthUser>;
|
|
21
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
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, resolveScopes, saveOAuthState } from "../auth.js";
|
|
4
|
+
const envPrefix = "AMAZON_COGNITO";
|
|
5
|
+
const defaultScopes = ["openid", "profile", "email"];
|
|
6
|
+
export class AmazonCognito {
|
|
7
|
+
authorizationEndpoint;
|
|
8
|
+
tokenEndpoint;
|
|
9
|
+
tokenRevocationEndpoint;
|
|
10
|
+
userinfoEndpoint;
|
|
11
|
+
client;
|
|
12
|
+
auth = null;
|
|
13
|
+
constructor(domainOrOptions, clientId, clientSecret, redirectURI) {
|
|
14
|
+
let domain;
|
|
15
|
+
if (typeof domainOrOptions === "object") {
|
|
16
|
+
this.auth = resolveAuthConfig(envPrefix, domainOrOptions, { redirectURI: true });
|
|
17
|
+
domain = requireProviderOption(domainOrOptions.domain, envPrefix, "DOMAIN", "domain");
|
|
18
|
+
this.client = new OAuth2Client(this.auth.clientId, this.auth.clientSecret, this.auth.redirectURI);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
domain = domainOrOptions;
|
|
22
|
+
this.client = new OAuth2Client(clientId ?? "", clientSecret ?? null, redirectURI ?? null);
|
|
23
|
+
}
|
|
24
|
+
this.authorizationEndpoint = `https://${domain}/oauth2/authorize`;
|
|
25
|
+
this.tokenEndpoint = `https://${domain}/oauth2/token`;
|
|
26
|
+
this.tokenRevocationEndpoint = `https://${domain}/oauth2/revoke`;
|
|
27
|
+
this.userinfoEndpoint = `https://${domain}/oauth2/userInfo`;
|
|
28
|
+
}
|
|
29
|
+
createAuthorizationURL(state, codeVerifier, scopes) {
|
|
30
|
+
const url = this.client.createAuthorizationURLWithPKCE(this.authorizationEndpoint, state, CodeChallengeMethod.S256, codeVerifier, scopes);
|
|
31
|
+
return url;
|
|
32
|
+
}
|
|
33
|
+
async validateAuthorizationCode(code, codeVerifier) {
|
|
34
|
+
const tokens = await this.client.validateAuthorizationCode(this.tokenEndpoint, code, codeVerifier);
|
|
35
|
+
return tokens;
|
|
36
|
+
}
|
|
37
|
+
async refreshAccessToken(refreshToken, scopes) {
|
|
38
|
+
const tokens = await this.client.refreshAccessToken(this.tokenEndpoint, refreshToken, scopes);
|
|
39
|
+
return tokens;
|
|
40
|
+
}
|
|
41
|
+
async revokeToken(token) {
|
|
42
|
+
await this.client.revokeToken(this.tokenRevocationEndpoint, token);
|
|
43
|
+
}
|
|
44
|
+
async getAuthorizationURL(scopes) {
|
|
45
|
+
const auth = requireAuthConfig(this.auth);
|
|
46
|
+
const state = generateOAuthState();
|
|
47
|
+
const codeVerifier = generateOAuthCodeVerifier();
|
|
48
|
+
const url = this.createAuthorizationURL(state, codeVerifier, resolveScopes(scopes, auth, defaultScopes));
|
|
49
|
+
await saveOAuthState(auth.store, state, { codeVerifier });
|
|
50
|
+
return url;
|
|
51
|
+
}
|
|
52
|
+
async getUser(query) {
|
|
53
|
+
const auth = requireAuthConfig(this.auth);
|
|
54
|
+
const { code, state } = parseCallbackQuery(query);
|
|
55
|
+
const stored = await consumeOAuthState(auth.store, state);
|
|
56
|
+
if (typeof stored.codeVerifier !== "string") {
|
|
57
|
+
throw new InvalidOAuthCallbackError("Missing PKCE code verifier for OAuth state");
|
|
58
|
+
}
|
|
59
|
+
const tokens = await this.validateAuthorizationCode(code, stored.codeVerifier);
|
|
60
|
+
let claims;
|
|
61
|
+
if ("id_token" in tokens.data) {
|
|
62
|
+
claims = decodeIdToken(tokens.idToken());
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
claims = await fetchUserProfile(this.userinfoEndpoint, tokens.accessToken());
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
id: profileId(claims.sub),
|
|
69
|
+
name: profileString(claims.name) ??
|
|
70
|
+
profileString(claims["cognito:username"]) ??
|
|
71
|
+
profileString(claims.username),
|
|
72
|
+
email: profileString(claims.email),
|
|
73
|
+
image: profileString(claims.picture)
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { OAuth2Tokens } from "../oauth2.js";
|
|
2
|
+
import type { OAuthCallbackQuery, OAuthUser, ProviderOptions } from "../auth.js";
|
|
3
|
+
export interface AniListOptions extends ProviderOptions {
|
|
4
|
+
}
|
|
5
|
+
export declare class AniList {
|
|
6
|
+
private client;
|
|
7
|
+
private auth;
|
|
8
|
+
constructor(options: AniListOptions);
|
|
9
|
+
constructor(clientId: string, clientSecret: string, redirectURI: string);
|
|
10
|
+
createAuthorizationURL(state: string): URL;
|
|
11
|
+
validateAuthorizationCode(code: string): Promise<OAuth2Tokens>;
|
|
12
|
+
getAuthorizationURL(): Promise<URL>;
|
|
13
|
+
getUser(query: OAuthCallbackQuery): Promise<OAuthUser>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { OAuth2Client } from "../client.js";
|
|
2
|
+
import { consumeOAuthState, generateOAuthState, OAuthProviderError, parseCallbackQuery, profileId, profileString, requireAuthConfig, resolveAuthConfig, saveOAuthState } from "../auth.js";
|
|
3
|
+
const authorizationEndpoint = "https://anilist.co/api/v2/oauth/authorize";
|
|
4
|
+
const tokenEndpoint = "https://anilist.co/api/v2/oauth/token";
|
|
5
|
+
const graphqlEndpoint = "https://graphql.anilist.co";
|
|
6
|
+
const envPrefix = "ANI_LIST";
|
|
7
|
+
export class AniList {
|
|
8
|
+
client;
|
|
9
|
+
auth = null;
|
|
10
|
+
constructor(clientIdOrOptions, clientSecret, redirectURI) {
|
|
11
|
+
if (typeof clientIdOrOptions === "object") {
|
|
12
|
+
this.auth = resolveAuthConfig(envPrefix, clientIdOrOptions, {
|
|
13
|
+
clientSecret: true,
|
|
14
|
+
redirectURI: true
|
|
15
|
+
});
|
|
16
|
+
this.client = new OAuth2Client(this.auth.clientId, this.auth.clientSecret, this.auth.redirectURI);
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
this.client = new OAuth2Client(clientIdOrOptions, clientSecret ?? null, redirectURI ?? null);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
createAuthorizationURL(state) {
|
|
23
|
+
const url = this.client.createAuthorizationURL(authorizationEndpoint, state, []);
|
|
24
|
+
return url;
|
|
25
|
+
}
|
|
26
|
+
async validateAuthorizationCode(code) {
|
|
27
|
+
const tokens = await this.client.validateAuthorizationCode(tokenEndpoint, code, null);
|
|
28
|
+
return tokens;
|
|
29
|
+
}
|
|
30
|
+
async getAuthorizationURL() {
|
|
31
|
+
const auth = requireAuthConfig(this.auth);
|
|
32
|
+
const state = generateOAuthState();
|
|
33
|
+
const url = this.createAuthorizationURL(state);
|
|
34
|
+
await saveOAuthState(auth.store, state, {});
|
|
35
|
+
return url;
|
|
36
|
+
}
|
|
37
|
+
async getUser(query) {
|
|
38
|
+
const auth = requireAuthConfig(this.auth);
|
|
39
|
+
const { code, state } = parseCallbackQuery(query);
|
|
40
|
+
await consumeOAuthState(auth.store, state);
|
|
41
|
+
const tokens = await this.validateAuthorizationCode(code);
|
|
42
|
+
const viewer = await fetchViewer(tokens.accessToken());
|
|
43
|
+
let image = null;
|
|
44
|
+
if (typeof viewer.avatar === "object" && viewer.avatar !== null) {
|
|
45
|
+
image = profileString(viewer.avatar.large);
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
id: profileId(viewer.id),
|
|
49
|
+
name: profileString(viewer.name),
|
|
50
|
+
email: null,
|
|
51
|
+
image
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// AniList only exposes the authenticated user through its GraphQL API.
|
|
56
|
+
async function fetchViewer(accessToken) {
|
|
57
|
+
let response;
|
|
58
|
+
try {
|
|
59
|
+
response = await fetch(graphqlEndpoint, {
|
|
60
|
+
method: "POST",
|
|
61
|
+
headers: {
|
|
62
|
+
Authorization: `Bearer ${accessToken}`,
|
|
63
|
+
"Content-Type": "application/json",
|
|
64
|
+
Accept: "application/json",
|
|
65
|
+
"User-Agent": "arctic"
|
|
66
|
+
},
|
|
67
|
+
body: JSON.stringify({ query: "query { Viewer { id name avatar { large } } }" })
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
throw new OAuthProviderError("Failed to fetch user profile");
|
|
72
|
+
}
|
|
73
|
+
if (!response.ok) {
|
|
74
|
+
if (response.body !== null) {
|
|
75
|
+
await response.body.cancel();
|
|
76
|
+
}
|
|
77
|
+
throw new OAuthProviderError(`User profile request failed with status ${response.status}`);
|
|
78
|
+
}
|
|
79
|
+
let data;
|
|
80
|
+
try {
|
|
81
|
+
data = await response.json();
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
throw new OAuthProviderError("User profile response was not valid JSON");
|
|
85
|
+
}
|
|
86
|
+
const viewer = data?.data?.Viewer;
|
|
87
|
+
if (typeof viewer !== "object" || viewer === null) {
|
|
88
|
+
throw new OAuthProviderError("Unexpected user profile response body");
|
|
89
|
+
}
|
|
90
|
+
return viewer;
|
|
91
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { OAuth2Tokens } from "../oauth2.js";
|
|
2
|
+
import type { OAuthCallbackQuery, OAuthUser, ProviderOptions } from "../auth.js";
|
|
3
|
+
export interface AppleOptions extends ProviderOptions {
|
|
4
|
+
teamId?: string;
|
|
5
|
+
keyId?: string;
|
|
6
|
+
pkcs8PrivateKey: Uint8Array;
|
|
7
|
+
}
|
|
8
|
+
export declare class Apple {
|
|
9
|
+
private clientId;
|
|
10
|
+
private teamId;
|
|
11
|
+
private keyId;
|
|
12
|
+
private pkcs8PrivateKey;
|
|
13
|
+
private redirectURI;
|
|
14
|
+
private auth;
|
|
15
|
+
constructor(options: AppleOptions);
|
|
16
|
+
constructor(clientId: string, teamId: string, keyId: string, pkcs8PrivateKey: Uint8Array, redirectURI: string);
|
|
17
|
+
createAuthorizationURL(state: string, scopes: string[]): URL;
|
|
18
|
+
validateAuthorizationCode(code: string): Promise<OAuth2Tokens>;
|
|
19
|
+
getAuthorizationURL(scopes?: string[]): Promise<URL>;
|
|
20
|
+
getUser(query: OAuthCallbackQuery): Promise<OAuthUser>;
|
|
21
|
+
private createClientSecret;
|
|
22
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import * as jwt from "@oslojs/jwt";
|
|
2
|
+
import { createOAuth2Request, sendTokenRequest } from "../request.js";
|
|
3
|
+
import { decodeIdToken } from "../oidc.js";
|
|
4
|
+
import { consumeOAuthState, generateOAuthState, parseCallbackQuery, profileId, profileString, requireAuthConfig, requireProviderOption, resolveAuthConfig, resolveScopes, saveOAuthState } from "../auth.js";
|
|
5
|
+
const authorizationEndpoint = "https://appleid.apple.com/auth/authorize";
|
|
6
|
+
const tokenEndpoint = "https://appleid.apple.com/auth/token";
|
|
7
|
+
const envPrefix = "APPLE";
|
|
8
|
+
const defaultScopes = ["email"];
|
|
9
|
+
export class Apple {
|
|
10
|
+
clientId;
|
|
11
|
+
teamId;
|
|
12
|
+
keyId;
|
|
13
|
+
pkcs8PrivateKey;
|
|
14
|
+
redirectURI;
|
|
15
|
+
auth = null;
|
|
16
|
+
constructor(clientIdOrOptions, teamId, keyId, pkcs8PrivateKey, redirectURI) {
|
|
17
|
+
if (typeof clientIdOrOptions === "object") {
|
|
18
|
+
this.auth = resolveAuthConfig(envPrefix, clientIdOrOptions, { redirectURI: true });
|
|
19
|
+
this.clientId = this.auth.clientId;
|
|
20
|
+
this.teamId = requireProviderOption(clientIdOrOptions.teamId, envPrefix, "TEAM_ID", "teamId");
|
|
21
|
+
this.keyId = requireProviderOption(clientIdOrOptions.keyId, envPrefix, "KEY_ID", "keyId");
|
|
22
|
+
this.pkcs8PrivateKey = clientIdOrOptions.pkcs8PrivateKey;
|
|
23
|
+
this.redirectURI = this.auth.redirectURI ?? "";
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
this.clientId = clientIdOrOptions;
|
|
27
|
+
this.teamId = teamId ?? "";
|
|
28
|
+
this.keyId = keyId ?? "";
|
|
29
|
+
this.pkcs8PrivateKey = pkcs8PrivateKey ?? new Uint8Array();
|
|
30
|
+
this.redirectURI = redirectURI ?? "";
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
createAuthorizationURL(state, scopes) {
|
|
34
|
+
const url = new URL(authorizationEndpoint);
|
|
35
|
+
url.searchParams.set("response_type", "code");
|
|
36
|
+
url.searchParams.set("client_id", this.clientId);
|
|
37
|
+
url.searchParams.set("state", state);
|
|
38
|
+
if (scopes.length > 0) {
|
|
39
|
+
url.searchParams.set("scope", scopes.join(" "));
|
|
40
|
+
}
|
|
41
|
+
url.searchParams.set("redirect_uri", this.redirectURI);
|
|
42
|
+
return url;
|
|
43
|
+
}
|
|
44
|
+
async validateAuthorizationCode(code) {
|
|
45
|
+
const body = new URLSearchParams();
|
|
46
|
+
body.set("grant_type", "authorization_code");
|
|
47
|
+
body.set("code", code);
|
|
48
|
+
body.set("redirect_uri", this.redirectURI);
|
|
49
|
+
body.set("client_id", this.clientId);
|
|
50
|
+
const clientSecret = await this.createClientSecret();
|
|
51
|
+
body.set("client_secret", clientSecret);
|
|
52
|
+
const request = createOAuth2Request(tokenEndpoint, body);
|
|
53
|
+
const tokens = await sendTokenRequest(request);
|
|
54
|
+
return tokens;
|
|
55
|
+
}
|
|
56
|
+
async getAuthorizationURL(scopes) {
|
|
57
|
+
const auth = requireAuthConfig(this.auth);
|
|
58
|
+
const state = generateOAuthState();
|
|
59
|
+
const resolvedScopes = resolveScopes(scopes, auth, defaultScopes);
|
|
60
|
+
const url = this.createAuthorizationURL(state, resolvedScopes);
|
|
61
|
+
// Apple requires form_post whenever scopes are requested.
|
|
62
|
+
if (resolvedScopes.length > 0) {
|
|
63
|
+
url.searchParams.set("response_mode", "form_post");
|
|
64
|
+
}
|
|
65
|
+
await saveOAuthState(auth.store, state, {});
|
|
66
|
+
return url;
|
|
67
|
+
}
|
|
68
|
+
async getUser(query) {
|
|
69
|
+
const auth = requireAuthConfig(this.auth);
|
|
70
|
+
const { code, state } = parseCallbackQuery(query);
|
|
71
|
+
await consumeOAuthState(auth.store, state);
|
|
72
|
+
const tokens = await this.validateAuthorizationCode(code);
|
|
73
|
+
const claims = decodeIdToken(tokens.idToken());
|
|
74
|
+
return {
|
|
75
|
+
id: profileId(claims.sub),
|
|
76
|
+
name: null,
|
|
77
|
+
email: profileString(claims.email),
|
|
78
|
+
image: null
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
async createClientSecret() {
|
|
82
|
+
const privateKey = await crypto.subtle.importKey("pkcs8", this.pkcs8PrivateKey, {
|
|
83
|
+
name: "ECDSA",
|
|
84
|
+
namedCurve: "P-256"
|
|
85
|
+
}, false, ["sign"]);
|
|
86
|
+
const now = Math.floor(Date.now() / 1000);
|
|
87
|
+
const headerJSON = JSON.stringify({
|
|
88
|
+
typ: "JWT",
|
|
89
|
+
alg: "ES256",
|
|
90
|
+
kid: this.keyId
|
|
91
|
+
});
|
|
92
|
+
const payloadJSON = JSON.stringify({
|
|
93
|
+
iss: this.teamId,
|
|
94
|
+
exp: now + 5 * 60,
|
|
95
|
+
aud: ["https://appleid.apple.com"],
|
|
96
|
+
sub: this.clientId,
|
|
97
|
+
iat: now
|
|
98
|
+
});
|
|
99
|
+
const signature = new Uint8Array(await crypto.subtle.sign({
|
|
100
|
+
name: "ECDSA",
|
|
101
|
+
hash: "SHA-256"
|
|
102
|
+
}, privateKey, jwt.createJWTSignatureMessage(headerJSON, payloadJSON)));
|
|
103
|
+
const token = jwt.encodeJWT(headerJSON, payloadJSON, signature);
|
|
104
|
+
return token;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { OAuth2Tokens } from "../oauth2.js";
|
|
2
|
+
import type { OAuthCallbackQuery, OAuthUser, ProviderOptions } from "../auth.js";
|
|
3
|
+
export interface AtlassianOptions extends ProviderOptions {
|
|
4
|
+
}
|
|
5
|
+
export declare class Atlassian {
|
|
6
|
+
private clientId;
|
|
7
|
+
private clientSecret;
|
|
8
|
+
private redirectURI;
|
|
9
|
+
private auth;
|
|
10
|
+
constructor(options: AtlassianOptions);
|
|
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
|
+
}
|