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,65 @@
|
|
|
1
|
+
import { OAuth2Client } from "../client.js";
|
|
2
|
+
import { decodeIdToken } from "../oidc.js";
|
|
3
|
+
import { consumeOAuthState, fetchUserProfile, generateOAuthState, parseCallbackQuery, profileId, profileString, requireAuthConfig, resolveAuthConfig, resolveScopes, saveOAuthState } from "../auth.js";
|
|
4
|
+
const authorizationEndpoint = "https://api.login.yahoo.com/oauth2/request_auth";
|
|
5
|
+
const tokenEndpoint = "https://api.login.yahoo.com/oauth2/get_token";
|
|
6
|
+
const userinfoEndpoint = "https://api.login.yahoo.com/openid/v1/userinfo";
|
|
7
|
+
const envPrefix = "YAHOO";
|
|
8
|
+
const defaultScopes = ["openid", "profile", "email"];
|
|
9
|
+
export class Yahoo {
|
|
10
|
+
client;
|
|
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.client = new OAuth2Client(this.auth.clientId, this.auth.clientSecret, this.auth.redirectURI);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
this.client = new OAuth2Client(clientIdOrOptions, clientSecret ?? "", redirectURI ?? "");
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
createAuthorizationURL(state, scopes) {
|
|
25
|
+
const url = this.client.createAuthorizationURL(authorizationEndpoint, state, scopes);
|
|
26
|
+
return url;
|
|
27
|
+
}
|
|
28
|
+
async validateAuthorizationCode(code) {
|
|
29
|
+
const tokens = await this.client.validateAuthorizationCode(tokenEndpoint, code, null);
|
|
30
|
+
return tokens;
|
|
31
|
+
}
|
|
32
|
+
async refreshAccessToken(refreshToken) {
|
|
33
|
+
const tokens = await this.client.refreshAccessToken(tokenEndpoint, refreshToken, []);
|
|
34
|
+
return tokens;
|
|
35
|
+
}
|
|
36
|
+
async getAuthorizationURL(scopes) {
|
|
37
|
+
const auth = requireAuthConfig(this.auth);
|
|
38
|
+
const state = generateOAuthState();
|
|
39
|
+
const url = this.createAuthorizationURL(state, resolveScopes(scopes, auth, defaultScopes));
|
|
40
|
+
await saveOAuthState(auth.store, state, {});
|
|
41
|
+
return url;
|
|
42
|
+
}
|
|
43
|
+
async getUser(query) {
|
|
44
|
+
const auth = requireAuthConfig(this.auth);
|
|
45
|
+
const { code, state } = parseCallbackQuery(query);
|
|
46
|
+
await consumeOAuthState(auth.store, state);
|
|
47
|
+
const tokens = await this.validateAuthorizationCode(code);
|
|
48
|
+
let claims;
|
|
49
|
+
if ("id_token" in tokens.data) {
|
|
50
|
+
claims = decodeIdToken(tokens.idToken());
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
claims = await fetchUserProfile(userinfoEndpoint, tokens.accessToken());
|
|
54
|
+
}
|
|
55
|
+
const givenName = profileString(claims.given_name);
|
|
56
|
+
const familyName = profileString(claims.family_name);
|
|
57
|
+
const fullName = [givenName, familyName].filter((part) => part !== null).join(" ");
|
|
58
|
+
return {
|
|
59
|
+
id: profileId(claims.sub),
|
|
60
|
+
name: profileString(claims.name) ?? (fullName !== "" ? fullName : null),
|
|
61
|
+
email: profileString(claims.email),
|
|
62
|
+
image: profileString(claims.picture)
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { OAuth2Tokens } from "../oauth2.js";
|
|
2
|
+
import type { OAuthCallbackQuery, OAuthUser, ProviderOptions } from "../auth.js";
|
|
3
|
+
export interface YandexOptions extends ProviderOptions {
|
|
4
|
+
}
|
|
5
|
+
export declare class Yandex {
|
|
6
|
+
private client;
|
|
7
|
+
private auth;
|
|
8
|
+
constructor(options: YandexOptions);
|
|
9
|
+
constructor(clientId: string, clientSecret: string, redirectURI: string);
|
|
10
|
+
createAuthorizationURL(state: string, scopes: string[]): URL;
|
|
11
|
+
validateAuthorizationCode(code: 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,66 @@
|
|
|
1
|
+
import { OAuth2Client } from "../client.js";
|
|
2
|
+
import { consumeOAuthState, fetchUserProfile, generateOAuthState, parseCallbackQuery, profileId, profileString, requireAuthConfig, resolveAuthConfig, resolveScopes, saveOAuthState } from "../auth.js";
|
|
3
|
+
const authorizationEndpoint = "https://oauth.yandex.com/authorize";
|
|
4
|
+
const tokenEndpoint = "https://oauth.yandex.com/token";
|
|
5
|
+
const userEndpoint = "https://login.yandex.ru/info?format=json";
|
|
6
|
+
const envPrefix = "YANDEX";
|
|
7
|
+
const defaultScopes = ["login:info", "login:email", "login:avatar"];
|
|
8
|
+
export class Yandex {
|
|
9
|
+
client;
|
|
10
|
+
auth = null;
|
|
11
|
+
constructor(clientIdOrOptions, clientSecret, redirectURI) {
|
|
12
|
+
if (typeof clientIdOrOptions === "object") {
|
|
13
|
+
this.auth = resolveAuthConfig(envPrefix, clientIdOrOptions, {
|
|
14
|
+
clientSecret: true,
|
|
15
|
+
redirectURI: true
|
|
16
|
+
});
|
|
17
|
+
this.client = new OAuth2Client(this.auth.clientId, this.auth.clientSecret, this.auth.redirectURI);
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
this.client = new OAuth2Client(clientIdOrOptions, clientSecret ?? "", redirectURI ?? "");
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
createAuthorizationURL(state, scopes) {
|
|
24
|
+
const url = this.client.createAuthorizationURL(authorizationEndpoint, state, scopes);
|
|
25
|
+
return url;
|
|
26
|
+
}
|
|
27
|
+
async validateAuthorizationCode(code) {
|
|
28
|
+
const tokens = await this.client.validateAuthorizationCode(tokenEndpoint, code, null);
|
|
29
|
+
return tokens;
|
|
30
|
+
}
|
|
31
|
+
async refreshAccessToken(refreshToken) {
|
|
32
|
+
const tokens = await this.client.refreshAccessToken(tokenEndpoint, refreshToken, []);
|
|
33
|
+
return tokens;
|
|
34
|
+
}
|
|
35
|
+
async getAuthorizationURL(scopes) {
|
|
36
|
+
const auth = requireAuthConfig(this.auth);
|
|
37
|
+
const state = generateOAuthState();
|
|
38
|
+
const url = this.createAuthorizationURL(state, resolveScopes(scopes, auth, defaultScopes));
|
|
39
|
+
await saveOAuthState(auth.store, state, {});
|
|
40
|
+
return url;
|
|
41
|
+
}
|
|
42
|
+
async getUser(query) {
|
|
43
|
+
const auth = requireAuthConfig(this.auth);
|
|
44
|
+
const { code, state } = parseCallbackQuery(query);
|
|
45
|
+
await consumeOAuthState(auth.store, state);
|
|
46
|
+
const tokens = await this.validateAuthorizationCode(code);
|
|
47
|
+
const accessToken = tokens.accessToken();
|
|
48
|
+
// Yandex expects the "OAuth" scheme instead of "Bearer".
|
|
49
|
+
const profile = await fetchUserProfile(userEndpoint, accessToken, {
|
|
50
|
+
Authorization: `OAuth ${accessToken}`
|
|
51
|
+
});
|
|
52
|
+
const avatarId = profileString(profile.default_avatar_id);
|
|
53
|
+
let image = null;
|
|
54
|
+
if (avatarId !== null && profile.is_avatar_empty !== true) {
|
|
55
|
+
image = `https://avatars.yandex.net/get-yapic/${avatarId}/islands-200`;
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
id: profileId(profile.id),
|
|
59
|
+
name: profileString(profile.real_name) ??
|
|
60
|
+
profileString(profile.display_name) ??
|
|
61
|
+
profileString(profile.login),
|
|
62
|
+
email: profileString(profile.default_email),
|
|
63
|
+
image
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { OAuth2Tokens } from "../oauth2.js";
|
|
2
|
+
import type { OAuthCallbackQuery, OAuthUser, ProviderOptions } from "../auth.js";
|
|
3
|
+
export interface ZoomOptions extends ProviderOptions {
|
|
4
|
+
}
|
|
5
|
+
export declare class Zoom {
|
|
6
|
+
private client;
|
|
7
|
+
private auth;
|
|
8
|
+
constructor(options: ZoomOptions);
|
|
9
|
+
constructor(clientId: string, clientSecret: string, redirectURI: string);
|
|
10
|
+
createAuthorizationURL(state: string, codeVerifier: string, scopes: string[]): URL;
|
|
11
|
+
validateAuthorizationCode(code: string, codeVerifier: string): Promise<OAuth2Tokens>;
|
|
12
|
+
refreshAccessToken(refreshToken: string): Promise<OAuth2Tokens>;
|
|
13
|
+
revokeToken(token: string): Promise<void>;
|
|
14
|
+
getAuthorizationURL(scopes?: string[]): Promise<URL>;
|
|
15
|
+
getUser(query: OAuthCallbackQuery): Promise<OAuthUser>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { OAuth2Client, CodeChallengeMethod } from "../client.js";
|
|
2
|
+
import { consumeOAuthState, fetchUserProfile, generateOAuthCodeVerifier, generateOAuthState, InvalidOAuthCallbackError, parseCallbackQuery, profileId, profileString, requireAuthConfig, resolveAuthConfig, resolveScopes, saveOAuthState } from "../auth.js";
|
|
3
|
+
const authorizationEndpoint = "https://zoom.us/oauth/authorize";
|
|
4
|
+
const tokenEndpoint = "https://zoom.us/oauth/token";
|
|
5
|
+
const tokenRevocationEndpoint = "https://zoom.us/oauth/revoke";
|
|
6
|
+
const userEndpoint = "https://api.zoom.us/v2/users/me";
|
|
7
|
+
const envPrefix = "ZOOM";
|
|
8
|
+
const defaultScopes = ["user:read:user"];
|
|
9
|
+
export class Zoom {
|
|
10
|
+
client;
|
|
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.client = new OAuth2Client(this.auth.clientId, this.auth.clientSecret, this.auth.redirectURI);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
this.client = new OAuth2Client(clientIdOrOptions, clientSecret ?? "", redirectURI ?? "");
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
createAuthorizationURL(state, codeVerifier, scopes) {
|
|
25
|
+
const url = this.client.createAuthorizationURLWithPKCE(authorizationEndpoint, state, CodeChallengeMethod.S256, codeVerifier, scopes);
|
|
26
|
+
return url;
|
|
27
|
+
}
|
|
28
|
+
async validateAuthorizationCode(code, codeVerifier) {
|
|
29
|
+
const tokens = await this.client.validateAuthorizationCode(tokenEndpoint, code, codeVerifier);
|
|
30
|
+
return tokens;
|
|
31
|
+
}
|
|
32
|
+
async refreshAccessToken(refreshToken) {
|
|
33
|
+
const tokens = await this.client.refreshAccessToken(tokenEndpoint, refreshToken, []);
|
|
34
|
+
return tokens;
|
|
35
|
+
}
|
|
36
|
+
async revokeToken(token) {
|
|
37
|
+
await this.client.revokeToken(tokenRevocationEndpoint, token);
|
|
38
|
+
}
|
|
39
|
+
async getAuthorizationURL(scopes) {
|
|
40
|
+
const auth = requireAuthConfig(this.auth);
|
|
41
|
+
const state = generateOAuthState();
|
|
42
|
+
const codeVerifier = generateOAuthCodeVerifier();
|
|
43
|
+
const url = this.createAuthorizationURL(state, codeVerifier, resolveScopes(scopes, auth, defaultScopes));
|
|
44
|
+
await saveOAuthState(auth.store, state, { codeVerifier });
|
|
45
|
+
return url;
|
|
46
|
+
}
|
|
47
|
+
async getUser(query) {
|
|
48
|
+
const auth = requireAuthConfig(this.auth);
|
|
49
|
+
const { code, state } = parseCallbackQuery(query);
|
|
50
|
+
const stored = await consumeOAuthState(auth.store, state);
|
|
51
|
+
if (typeof stored.codeVerifier !== "string") {
|
|
52
|
+
throw new InvalidOAuthCallbackError("Missing PKCE code verifier for OAuth state");
|
|
53
|
+
}
|
|
54
|
+
const tokens = await this.validateAuthorizationCode(code, stored.codeVerifier);
|
|
55
|
+
const profile = await fetchUserProfile(userEndpoint, tokens.accessToken());
|
|
56
|
+
const firstName = profileString(profile.first_name);
|
|
57
|
+
const lastName = profileString(profile.last_name);
|
|
58
|
+
const fullName = [firstName, lastName].filter((part) => part !== null).join(" ");
|
|
59
|
+
return {
|
|
60
|
+
id: profileId(profile.id),
|
|
61
|
+
name: profileString(profile.display_name) ?? (fullName !== "" ? fullName : null),
|
|
62
|
+
email: profileString(profile.email),
|
|
63
|
+
image: profileString(profile.pic_url)
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { OAuth2Tokens } from "./oauth2.js";
|
|
2
|
+
export declare function joinURIAndPath(base: string, ...path: string[]): string;
|
|
3
|
+
export declare function createOAuth2Request(endpoint: string, body: URLSearchParams): Request;
|
|
4
|
+
export declare function encodeBasicCredentials(username: string, password: string): string;
|
|
5
|
+
export declare function sendTokenRequest(request: Request): Promise<OAuth2Tokens>;
|
|
6
|
+
export declare function sendTokenRevocationRequest(request: Request): Promise<void>;
|
|
7
|
+
export declare function createOAuth2RequestError(result: object): OAuth2RequestError;
|
|
8
|
+
export declare class ArcticFetchError extends Error {
|
|
9
|
+
constructor(cause: unknown);
|
|
10
|
+
}
|
|
11
|
+
export declare class OAuth2RequestError extends Error {
|
|
12
|
+
code: string;
|
|
13
|
+
description: string | null;
|
|
14
|
+
uri: string | null;
|
|
15
|
+
state: string | null;
|
|
16
|
+
constructor(code: string, description: string | null, uri: string | null, state: string | null);
|
|
17
|
+
}
|
|
18
|
+
export declare class UnexpectedResponseError extends Error {
|
|
19
|
+
status: number;
|
|
20
|
+
constructor(responseStatus: number);
|
|
21
|
+
}
|
|
22
|
+
export declare class UnexpectedErrorResponseBodyError extends Error {
|
|
23
|
+
status: number;
|
|
24
|
+
data: unknown;
|
|
25
|
+
constructor(status: number, data: unknown);
|
|
26
|
+
}
|
package/dist/request.js
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import * as encoding from "@oslojs/encoding";
|
|
2
|
+
import { OAuth2Tokens } from "./oauth2.js";
|
|
3
|
+
import { trimLeft, trimRight } from "./utils.js";
|
|
4
|
+
export function joinURIAndPath(base, ...path) {
|
|
5
|
+
let joined = trimRight(base, "/");
|
|
6
|
+
for (const part of path) {
|
|
7
|
+
joined = trimRight(joined, "/") + "/" + trimLeft(part, "/");
|
|
8
|
+
}
|
|
9
|
+
return joined;
|
|
10
|
+
}
|
|
11
|
+
export function createOAuth2Request(endpoint, body) {
|
|
12
|
+
const bodyBytes = new TextEncoder().encode(body.toString());
|
|
13
|
+
const request = new Request(endpoint, {
|
|
14
|
+
method: "POST",
|
|
15
|
+
body: bodyBytes
|
|
16
|
+
});
|
|
17
|
+
request.headers.set("Content-Type", "application/x-www-form-urlencoded");
|
|
18
|
+
request.headers.set("Accept", "application/json");
|
|
19
|
+
// Required by GitHub, and probably by others as well
|
|
20
|
+
request.headers.set("User-Agent", "arctic");
|
|
21
|
+
// Required by Reddit
|
|
22
|
+
request.headers.set("Content-Length", bodyBytes.byteLength.toString());
|
|
23
|
+
return request;
|
|
24
|
+
}
|
|
25
|
+
export function encodeBasicCredentials(username, password) {
|
|
26
|
+
const bytes = new TextEncoder().encode(`${username}:${password}`);
|
|
27
|
+
return encoding.encodeBase64(bytes);
|
|
28
|
+
}
|
|
29
|
+
export async function sendTokenRequest(request) {
|
|
30
|
+
let response;
|
|
31
|
+
try {
|
|
32
|
+
response = await fetch(request);
|
|
33
|
+
}
|
|
34
|
+
catch (e) {
|
|
35
|
+
throw new ArcticFetchError(e);
|
|
36
|
+
}
|
|
37
|
+
if (response.status === 400 || response.status === 401) {
|
|
38
|
+
let data;
|
|
39
|
+
try {
|
|
40
|
+
data = await response.json();
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
throw new UnexpectedResponseError(response.status);
|
|
44
|
+
}
|
|
45
|
+
if (typeof data !== "object" || data === null) {
|
|
46
|
+
throw new UnexpectedErrorResponseBodyError(response.status, data);
|
|
47
|
+
}
|
|
48
|
+
let error;
|
|
49
|
+
try {
|
|
50
|
+
error = createOAuth2RequestError(data);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
throw new UnexpectedErrorResponseBodyError(response.status, data);
|
|
54
|
+
}
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
if (response.status === 200) {
|
|
58
|
+
let data;
|
|
59
|
+
try {
|
|
60
|
+
data = await response.json();
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
throw new UnexpectedResponseError(response.status);
|
|
64
|
+
}
|
|
65
|
+
if (typeof data !== "object" || data === null) {
|
|
66
|
+
throw new UnexpectedErrorResponseBodyError(response.status, data);
|
|
67
|
+
}
|
|
68
|
+
const tokens = new OAuth2Tokens(data);
|
|
69
|
+
return tokens;
|
|
70
|
+
}
|
|
71
|
+
if (response.body !== null) {
|
|
72
|
+
await response.body.cancel();
|
|
73
|
+
}
|
|
74
|
+
throw new UnexpectedResponseError(response.status);
|
|
75
|
+
}
|
|
76
|
+
export async function sendTokenRevocationRequest(request) {
|
|
77
|
+
let response;
|
|
78
|
+
try {
|
|
79
|
+
response = await fetch(request);
|
|
80
|
+
}
|
|
81
|
+
catch (e) {
|
|
82
|
+
throw new ArcticFetchError(e);
|
|
83
|
+
}
|
|
84
|
+
if (response.status === 400 || response.status === 401) {
|
|
85
|
+
let data;
|
|
86
|
+
try {
|
|
87
|
+
data = await response.json();
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
throw new UnexpectedErrorResponseBodyError(response.status, null);
|
|
91
|
+
}
|
|
92
|
+
if (typeof data !== "object" || data === null) {
|
|
93
|
+
throw new UnexpectedErrorResponseBodyError(response.status, data);
|
|
94
|
+
}
|
|
95
|
+
let error;
|
|
96
|
+
try {
|
|
97
|
+
error = createOAuth2RequestError(data);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
throw new UnexpectedErrorResponseBodyError(response.status, data);
|
|
101
|
+
}
|
|
102
|
+
throw error;
|
|
103
|
+
}
|
|
104
|
+
if (response.status === 200) {
|
|
105
|
+
if (response.body !== null) {
|
|
106
|
+
await response.body.cancel();
|
|
107
|
+
}
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (response.body !== null) {
|
|
111
|
+
await response.body.cancel();
|
|
112
|
+
}
|
|
113
|
+
throw new UnexpectedResponseError(response.status);
|
|
114
|
+
}
|
|
115
|
+
export function createOAuth2RequestError(result) {
|
|
116
|
+
let code;
|
|
117
|
+
if ("error" in result && typeof result.error === "string") {
|
|
118
|
+
code = result.error;
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
throw new Error("Invalid error response");
|
|
122
|
+
}
|
|
123
|
+
let description = null;
|
|
124
|
+
let uri = null;
|
|
125
|
+
let state = null;
|
|
126
|
+
if ("error_description" in result) {
|
|
127
|
+
if (typeof result.error_description !== "string") {
|
|
128
|
+
throw new Error("Invalid data");
|
|
129
|
+
}
|
|
130
|
+
description = result.error_description;
|
|
131
|
+
}
|
|
132
|
+
if ("error_uri" in result) {
|
|
133
|
+
if (typeof result.error_uri !== "string") {
|
|
134
|
+
throw new Error("Invalid data");
|
|
135
|
+
}
|
|
136
|
+
uri = result.error_uri;
|
|
137
|
+
}
|
|
138
|
+
if ("state" in result) {
|
|
139
|
+
if (typeof result.state !== "string") {
|
|
140
|
+
throw new Error("Invalid data");
|
|
141
|
+
}
|
|
142
|
+
state = result.state;
|
|
143
|
+
}
|
|
144
|
+
const error = new OAuth2RequestError(code, description, uri, state);
|
|
145
|
+
return error;
|
|
146
|
+
}
|
|
147
|
+
export class ArcticFetchError extends Error {
|
|
148
|
+
constructor(cause) {
|
|
149
|
+
super("Failed to send request", {
|
|
150
|
+
cause
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
export class OAuth2RequestError extends Error {
|
|
155
|
+
code;
|
|
156
|
+
description;
|
|
157
|
+
uri;
|
|
158
|
+
state;
|
|
159
|
+
constructor(code, description, uri, state) {
|
|
160
|
+
super(`OAuth request error: ${code}`);
|
|
161
|
+
this.code = code;
|
|
162
|
+
this.description = description;
|
|
163
|
+
this.uri = uri;
|
|
164
|
+
this.state = state;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
export class UnexpectedResponseError extends Error {
|
|
168
|
+
status;
|
|
169
|
+
constructor(responseStatus) {
|
|
170
|
+
super("Unexpected error response");
|
|
171
|
+
this.status = responseStatus;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
export class UnexpectedErrorResponseBodyError extends Error {
|
|
175
|
+
status;
|
|
176
|
+
data;
|
|
177
|
+
constructor(status, data) {
|
|
178
|
+
super("Unexpected error response body");
|
|
179
|
+
this.status = status;
|
|
180
|
+
this.data = data;
|
|
181
|
+
}
|
|
182
|
+
}
|
package/dist/utils.d.ts
ADDED
package/dist/utils.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export function trimLeft(s, character) {
|
|
2
|
+
if (character.length !== 1) {
|
|
3
|
+
throw new TypeError("Invalid character string");
|
|
4
|
+
}
|
|
5
|
+
let start = 0;
|
|
6
|
+
while (start < s.length && s[start] === character) {
|
|
7
|
+
start++;
|
|
8
|
+
}
|
|
9
|
+
return s.slice(start);
|
|
10
|
+
}
|
|
11
|
+
export function trimRight(s, character) {
|
|
12
|
+
if (character.length !== 1) {
|
|
13
|
+
throw new TypeError("Invalid character string");
|
|
14
|
+
}
|
|
15
|
+
let end = s.length;
|
|
16
|
+
while (end > 0 && s[end - 1] === character) {
|
|
17
|
+
end--;
|
|
18
|
+
}
|
|
19
|
+
return s.slice(0, end);
|
|
20
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "antarctic",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "High-level OAuth 2.0 clients for popular providers, forked from Arctic",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"module": "dist/index.js",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "rm -rf dist/* && tsc",
|
|
11
|
+
"format": "prettier -w .",
|
|
12
|
+
"lint": "eslint src",
|
|
13
|
+
"test": "vitest run --sequence.concurrent"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"/dist/"
|
|
17
|
+
],
|
|
18
|
+
"author": "Francisco Presencia",
|
|
19
|
+
"contributors": [
|
|
20
|
+
"pilcrowOnPaper (author of Arctic, which Antarctic is a fork of)"
|
|
21
|
+
],
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git+https://github.com/franciscop/antarctic.git"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/node": "^20.8.6",
|
|
29
|
+
"@typescript-eslint/eslint-plugin": "^6.7.5",
|
|
30
|
+
"@typescript-eslint/parser": "^6.7.5",
|
|
31
|
+
"auri": "^3.1.0",
|
|
32
|
+
"eslint": "^8.51.0",
|
|
33
|
+
"prettier": "^3.0.3",
|
|
34
|
+
"typescript": "^5.2.2",
|
|
35
|
+
"vitest": "1.6.0"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@oslojs/crypto": "1.0.1",
|
|
39
|
+
"@oslojs/encoding": "1.1.0",
|
|
40
|
+
"@oslojs/jwt": "0.2.0"
|
|
41
|
+
}
|
|
42
|
+
}
|