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.
Files changed (145) hide show
  1. package/LICENSE +24 -0
  2. package/README.md +155 -0
  3. package/dist/auth.d.ts +63 -0
  4. package/dist/auth.js +191 -0
  5. package/dist/client.d.ts +16 -0
  6. package/dist/client.js +105 -0
  7. package/dist/index.d.ts +70 -0
  8. package/dist/index.js +69 -0
  9. package/dist/oauth2.d.ts +16 -0
  10. package/dist/oauth2.js +67 -0
  11. package/dist/oidc.d.ts +1 -0
  12. package/dist/oidc.js +11 -0
  13. package/dist/providers/42.d.ts +16 -0
  14. package/dist/providers/42.js +75 -0
  15. package/dist/providers/amazon-cognito.d.ts +21 -0
  16. package/dist/providers/amazon-cognito.js +76 -0
  17. package/dist/providers/anilist.d.ts +14 -0
  18. package/dist/providers/anilist.js +91 -0
  19. package/dist/providers/apple.d.ts +22 -0
  20. package/dist/providers/apple.js +106 -0
  21. package/dist/providers/atlassian.d.ts +17 -0
  22. package/dist/providers/atlassian.js +83 -0
  23. package/dist/providers/auth0.d.ts +21 -0
  24. package/dist/providers/auth0.js +80 -0
  25. package/dist/providers/authentik.d.ts +21 -0
  26. package/dist/providers/authentik.js +75 -0
  27. package/dist/providers/autodesk.d.ts +16 -0
  28. package/dist/providers/autodesk.js +60 -0
  29. package/dist/providers/battlenet.d.ts +16 -0
  30. package/dist/providers/battlenet.js +69 -0
  31. package/dist/providers/bitbucket.d.ts +15 -0
  32. package/dist/providers/bitbucket.js +79 -0
  33. package/dist/providers/box.d.ts +18 -0
  34. package/dist/providers/box.js +90 -0
  35. package/dist/providers/bungie.d.ts +17 -0
  36. package/dist/providers/bungie.js +72 -0
  37. package/dist/providers/coinbase.d.ts +18 -0
  38. package/dist/providers/coinbase.js +95 -0
  39. package/dist/providers/discord.d.ts +16 -0
  40. package/dist/providers/discord.js +68 -0
  41. package/dist/providers/donation-alerts.d.ts +17 -0
  42. package/dist/providers/donation-alerts.js +86 -0
  43. package/dist/providers/dribbble.d.ts +16 -0
  44. package/dist/providers/dribbble.js +73 -0
  45. package/dist/providers/dropbox.d.ts +16 -0
  46. package/dist/providers/dropbox.js +96 -0
  47. package/dist/providers/epicgames.d.ts +16 -0
  48. package/dist/providers/epicgames.js +60 -0
  49. package/dist/providers/etsy.d.ts +15 -0
  50. package/dist/providers/etsy.js +68 -0
  51. package/dist/providers/facebook.d.ts +16 -0
  52. package/dist/providers/facebook.js +80 -0
  53. package/dist/providers/figma.d.ts +15 -0
  54. package/dist/providers/figma.js +56 -0
  55. package/dist/providers/gitea.d.ts +19 -0
  56. package/dist/providers/gitea.js +62 -0
  57. package/dist/providers/github.d.ts +17 -0
  58. package/dist/providers/github.js +142 -0
  59. package/dist/providers/gitlab.d.ts +21 -0
  60. package/dist/providers/gitlab.js +64 -0
  61. package/dist/providers/google.d.ts +16 -0
  62. package/dist/providers/google.js +70 -0
  63. package/dist/providers/intuit.d.ts +16 -0
  64. package/dist/providers/intuit.js +66 -0
  65. package/dist/providers/kakao.d.ts +17 -0
  66. package/dist/providers/kakao.js +95 -0
  67. package/dist/providers/keycloak.d.ts +21 -0
  68. package/dist/providers/keycloak.js +74 -0
  69. package/dist/providers/kick.d.ts +18 -0
  70. package/dist/providers/kick.js +103 -0
  71. package/dist/providers/lichess.d.ts +14 -0
  72. package/dist/providers/lichess.js +63 -0
  73. package/dist/providers/line.d.ts +17 -0
  74. package/dist/providers/line.js +98 -0
  75. package/dist/providers/linear.d.ts +16 -0
  76. package/dist/providers/linear.js +112 -0
  77. package/dist/providers/linkedin.d.ts +17 -0
  78. package/dist/providers/linkedin.js +89 -0
  79. package/dist/providers/mastodon.d.ts +20 -0
  80. package/dist/providers/mastodon.js +67 -0
  81. package/dist/providers/mercadolibre.d.ts +17 -0
  82. package/dist/providers/mercadolibre.js +95 -0
  83. package/dist/providers/mercadopago.d.ts +17 -0
  84. package/dist/providers/mercadopago.js +95 -0
  85. package/dist/providers/microsoft-entra-id.d.ts +20 -0
  86. package/dist/providers/microsoft-entra-id.js +120 -0
  87. package/dist/providers/myanimelist.d.ts +15 -0
  88. package/dist/providers/myanimelist.js +56 -0
  89. package/dist/providers/naver.d.ts +17 -0
  90. package/dist/providers/naver.js +83 -0
  91. package/dist/providers/notion.d.ts +14 -0
  92. package/dist/providers/notion.js +65 -0
  93. package/dist/providers/okta.d.ts +22 -0
  94. package/dist/providers/okta.js +85 -0
  95. package/dist/providers/osu.d.ts +17 -0
  96. package/dist/providers/osu.js +82 -0
  97. package/dist/providers/patreon.d.ts +17 -0
  98. package/dist/providers/patreon.js +89 -0
  99. package/dist/providers/polar.d.ts +18 -0
  100. package/dist/providers/polar.js +100 -0
  101. package/dist/providers/reddit.d.ts +15 -0
  102. package/dist/providers/reddit.js +55 -0
  103. package/dist/providers/roblox.d.ts +16 -0
  104. package/dist/providers/roblox.js +67 -0
  105. package/dist/providers/salesforce.d.ts +21 -0
  106. package/dist/providers/salesforce.js +74 -0
  107. package/dist/providers/shikimori.d.ts +17 -0
  108. package/dist/providers/shikimori.js +81 -0
  109. package/dist/providers/slack.d.ts +14 -0
  110. package/dist/providers/slack.js +59 -0
  111. package/dist/providers/spotify.d.ts +15 -0
  112. package/dist/providers/spotify.js +67 -0
  113. package/dist/providers/startgg.d.ts +17 -0
  114. package/dist/providers/startgg.js +143 -0
  115. package/dist/providers/strava.d.ts +17 -0
  116. package/dist/providers/strava.js +85 -0
  117. package/dist/providers/synology.d.ts +9 -0
  118. package/dist/providers/synology.js +20 -0
  119. package/dist/providers/tiktok.d.ts +18 -0
  120. package/dist/providers/tiktok.js +109 -0
  121. package/dist/providers/tiltify.d.ts +17 -0
  122. package/dist/providers/tiltify.js +90 -0
  123. package/dist/providers/tumblr.d.ts +15 -0
  124. package/dist/providers/tumblr.js +63 -0
  125. package/dist/providers/twitch.d.ts +17 -0
  126. package/dist/providers/twitch.js +91 -0
  127. package/dist/providers/twitter.d.ts +16 -0
  128. package/dist/providers/twitter.js +67 -0
  129. package/dist/providers/vk.d.ts +16 -0
  130. package/dist/providers/vk.js +82 -0
  131. package/dist/providers/withings.d.ts +16 -0
  132. package/dist/providers/withings.js +120 -0
  133. package/dist/providers/workos.d.ts +16 -0
  134. package/dist/providers/workos.js +82 -0
  135. package/dist/providers/yahoo.d.ts +15 -0
  136. package/dist/providers/yahoo.js +65 -0
  137. package/dist/providers/yandex.d.ts +15 -0
  138. package/dist/providers/yandex.js +66 -0
  139. package/dist/providers/zoom.d.ts +16 -0
  140. package/dist/providers/zoom.js +66 -0
  141. package/dist/request.d.ts +26 -0
  142. package/dist/request.js +182 -0
  143. package/dist/utils.d.ts +2 -0
  144. package/dist/utils.js +20 -0
  145. package/package.json +42 -0
@@ -0,0 +1,63 @@
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://www.tumblr.com/oauth2/authorize";
4
+ const tokenEndpoint = "https://api.tumblr.com/v2/oauth2/token";
5
+ const userEndpoint = "https://api.tumblr.com/v2/user/info";
6
+ const envPrefix = "TUMBLR";
7
+ const defaultScopes = ["basic"];
8
+ export class Tumblr {
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 ?? null, redirectURI ?? null);
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 profile = await fetchUserProfile(userEndpoint, tokens.accessToken());
48
+ let user = {};
49
+ if (typeof profile.response === "object" && profile.response !== null) {
50
+ const inner = profile.response.user;
51
+ if (typeof inner === "object" && inner !== null) {
52
+ user = inner;
53
+ }
54
+ }
55
+ // Tumblr only exposes the username: no stable numeric id, email, or avatar.
56
+ return {
57
+ id: profileId(user.name),
58
+ name: profileString(user.name),
59
+ email: null,
60
+ image: null
61
+ };
62
+ }
63
+ }
@@ -0,0 +1,17 @@
1
+ import type { OAuth2Tokens } from "../oauth2.js";
2
+ import type { OAuthCallbackQuery, OAuthUser, ProviderOptions } from "../auth.js";
3
+ export interface TwitchOptions extends ProviderOptions {
4
+ }
5
+ export declare class Twitch {
6
+ private clientId;
7
+ private clientSecret;
8
+ private redirectURI;
9
+ private auth;
10
+ constructor(options: TwitchOptions);
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
+ }
@@ -0,0 +1,91 @@
1
+ // Does not support HTTP Basic Auth scheme.
2
+ import { createOAuth2Request, sendTokenRequest } from "../request.js";
3
+ import { consumeOAuthState, fetchUserProfile, generateOAuthState, OAuthProviderError, parseCallbackQuery, profileId, profileString, requireAuthConfig, resolveAuthConfig, resolveScopes, saveOAuthState } from "../auth.js";
4
+ const authorizationEndpoint = "https://id.twitch.tv/oauth2/authorize";
5
+ const tokenEndpoint = "https://id.twitch.tv/oauth2/token";
6
+ const userEndpoint = "https://api.twitch.tv/helix/users";
7
+ const envPrefix = "TWITCH";
8
+ const defaultScopes = ["user:read:email"];
9
+ export class Twitch {
10
+ clientId;
11
+ clientSecret;
12
+ redirectURI;
13
+ auth = null;
14
+ constructor(clientIdOrOptions, clientSecret, redirectURI) {
15
+ if (typeof clientIdOrOptions === "object") {
16
+ this.auth = resolveAuthConfig(envPrefix, clientIdOrOptions, {
17
+ clientSecret: true,
18
+ redirectURI: true
19
+ });
20
+ this.clientId = this.auth.clientId;
21
+ this.clientSecret = this.auth.clientSecret ?? "";
22
+ this.redirectURI = this.auth.redirectURI ?? "";
23
+ }
24
+ else {
25
+ this.clientId = clientIdOrOptions;
26
+ this.clientSecret = clientSecret ?? "";
27
+ this.redirectURI = redirectURI ?? "";
28
+ }
29
+ }
30
+ createAuthorizationURL(state, scopes) {
31
+ const url = new URL(authorizationEndpoint);
32
+ url.searchParams.set("response_type", "code");
33
+ url.searchParams.set("client_id", this.clientId);
34
+ url.searchParams.set("state", state);
35
+ if (scopes.length > 0) {
36
+ url.searchParams.set("scope", scopes.join(" "));
37
+ }
38
+ url.searchParams.set("redirect_uri", this.redirectURI);
39
+ return url;
40
+ }
41
+ async validateAuthorizationCode(code) {
42
+ const body = new URLSearchParams();
43
+ body.set("grant_type", "authorization_code");
44
+ body.set("code", code);
45
+ body.set("redirect_uri", this.redirectURI);
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
+ // No token revocation since the error responses are not spec-compliant
63
+ async getAuthorizationURL(scopes) {
64
+ const auth = requireAuthConfig(this.auth);
65
+ const state = generateOAuthState();
66
+ const url = this.createAuthorizationURL(state, resolveScopes(scopes, auth, defaultScopes));
67
+ await saveOAuthState(auth.store, state, {});
68
+ return url;
69
+ }
70
+ async getUser(query) {
71
+ const auth = requireAuthConfig(this.auth);
72
+ const { code, state } = parseCallbackQuery(query);
73
+ await consumeOAuthState(auth.store, state);
74
+ const tokens = await this.validateAuthorizationCode(code);
75
+ // Helix requires the Client-Id header alongside the user access token.
76
+ const profile = await fetchUserProfile(userEndpoint, tokens.accessToken(), {
77
+ "Client-Id": this.clientId
78
+ });
79
+ const entries = profile.data;
80
+ if (!Array.isArray(entries) || typeof entries[0] !== "object" || entries[0] === null) {
81
+ throw new OAuthProviderError("Unexpected user profile response body");
82
+ }
83
+ const user = entries[0];
84
+ return {
85
+ id: profileId(user.id),
86
+ name: profileString(user.display_name) ?? profileString(user.login),
87
+ email: profileString(user.email),
88
+ image: profileString(user.profile_image_url)
89
+ };
90
+ }
91
+ }
@@ -0,0 +1,16 @@
1
+ import type { OAuth2Tokens } from "../oauth2.js";
2
+ import type { OAuthCallbackQuery, OAuthUser, ProviderOptions } from "../auth.js";
3
+ export interface TwitterOptions extends ProviderOptions {
4
+ }
5
+ export declare class Twitter {
6
+ private client;
7
+ private auth;
8
+ constructor(options: TwitterOptions);
9
+ constructor(clientId: string, clientSecret: string | null, 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,67 @@
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://twitter.com/i/oauth2/authorize";
4
+ const tokenEndpoint = "https://api.twitter.com/2/oauth2/token";
5
+ const tokenRevocationEndpoint = "https://api.twitter.com/2/oauth2/revoke";
6
+ const userEndpoint = "https://api.twitter.com/2/users/me";
7
+ const envPrefix = "TWITTER";
8
+ const defaultScopes = ["users.read", "tweet.read"];
9
+ export class Twitter {
10
+ client;
11
+ auth = null;
12
+ constructor(clientIdOrOptions, clientSecret, redirectURI) {
13
+ if (typeof clientIdOrOptions === "object") {
14
+ this.auth = resolveAuthConfig(envPrefix, clientIdOrOptions, { redirectURI: true });
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, codeVerifier, scopes) {
22
+ const url = this.client.createAuthorizationURLWithPKCE(authorizationEndpoint, state, CodeChallengeMethod.S256, codeVerifier, scopes);
23
+ return url;
24
+ }
25
+ async validateAuthorizationCode(code, codeVerifier) {
26
+ const tokens = await this.client.validateAuthorizationCode(tokenEndpoint, code, codeVerifier);
27
+ return tokens;
28
+ }
29
+ async refreshAccessToken(refreshToken) {
30
+ const tokens = await this.client.refreshAccessToken(tokenEndpoint, refreshToken, []);
31
+ return tokens;
32
+ }
33
+ async revokeToken(token) {
34
+ await this.client.revokeToken(tokenRevocationEndpoint, token);
35
+ }
36
+ async getAuthorizationURL(scopes) {
37
+ const auth = requireAuthConfig(this.auth);
38
+ const state = generateOAuthState();
39
+ const codeVerifier = generateOAuthCodeVerifier();
40
+ const url = this.createAuthorizationURL(state, codeVerifier, resolveScopes(scopes, auth, defaultScopes));
41
+ await saveOAuthState(auth.store, state, { codeVerifier });
42
+ return url;
43
+ }
44
+ async getUser(query) {
45
+ const auth = requireAuthConfig(this.auth);
46
+ const { code, state } = parseCallbackQuery(query);
47
+ const stored = await consumeOAuthState(auth.store, state);
48
+ if (typeof stored.codeVerifier !== "string") {
49
+ throw new InvalidOAuthCallbackError("Missing PKCE code verifier for OAuth state");
50
+ }
51
+ const tokens = await this.validateAuthorizationCode(code, stored.codeVerifier);
52
+ const url = new URL(userEndpoint);
53
+ url.searchParams.set("user.fields", "profile_image_url");
54
+ const profile = await fetchUserProfile(url, tokens.accessToken());
55
+ let user = {};
56
+ if (typeof profile.data === "object" && profile.data !== null) {
57
+ user = profile.data;
58
+ }
59
+ // The Twitter API does not expose an email address.
60
+ return {
61
+ id: profileId(user.id),
62
+ name: profileString(user.name) ?? profileString(user.username),
63
+ email: null,
64
+ image: profileString(user.profile_image_url)
65
+ };
66
+ }
67
+ }
@@ -0,0 +1,16 @@
1
+ import type { OAuth2Tokens } from "../oauth2.js";
2
+ import type { OAuthCallbackQuery, OAuthUser, ProviderOptions } from "../auth.js";
3
+ export interface VKOptions extends ProviderOptions {
4
+ }
5
+ export declare class VK {
6
+ private clientId;
7
+ private clientSecret;
8
+ private redirectURI;
9
+ private auth;
10
+ constructor(options: VKOptions);
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,82 @@
1
+ import { createOAuth2Request, sendTokenRequest } from "../request.js";
2
+ import { consumeOAuthState, fetchUserProfile, generateOAuthState, OAuthProviderError, parseCallbackQuery, profileId, profileString, requireAuthConfig, resolveAuthConfig, resolveScopes, saveOAuthState } from "../auth.js";
3
+ const authorizationEndpoint = "https://oauth.vk.com/authorize";
4
+ const tokenEndpoint = "https://oauth.vk.com/access_token";
5
+ const userEndpoint = "https://api.vk.com/method/users.get";
6
+ const envPrefix = "VK";
7
+ const defaultScopes = ["email"];
8
+ export class VK {
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 url = new URL(userEndpoint);
64
+ url.searchParams.set("v", "5.199");
65
+ url.searchParams.set("fields", "photo_200");
66
+ const profile = await fetchUserProfile(url, tokens.accessToken());
67
+ const entries = profile.response;
68
+ if (!Array.isArray(entries) || typeof entries[0] !== "object" || entries[0] === null) {
69
+ throw new OAuthProviderError("Unexpected user profile response body");
70
+ }
71
+ const user = entries[0];
72
+ const nameParts = [profileString(user.first_name), profileString(user.last_name)].filter((part) => part !== null);
73
+ // The email is only returned in the token response, not by the API.
74
+ const email = profileString(tokens.data.email);
75
+ return {
76
+ id: profileId(user.id),
77
+ name: nameParts.length > 0 ? nameParts.join(" ") : null,
78
+ email,
79
+ image: profileString(user.photo_200)
80
+ };
81
+ }
82
+ }
@@ -0,0 +1,16 @@
1
+ import { OAuth2Tokens } from "../oauth2.js";
2
+ import type { OAuthCallbackQuery, OAuthUser, ProviderOptions } from "../auth.js";
3
+ export interface WithingsOptions extends ProviderOptions {
4
+ }
5
+ export declare class Withings {
6
+ private clientId;
7
+ private clientSecret;
8
+ private redirectURI;
9
+ private auth;
10
+ constructor(options: WithingsOptions);
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,120 @@
1
+ import { ArcticFetchError, createOAuth2Request, createOAuth2RequestError, UnexpectedErrorResponseBodyError, UnexpectedResponseError } from "../request.js";
2
+ import { OAuth2Tokens } from "../oauth2.js";
3
+ import { consumeOAuthState, generateOAuthState, parseCallbackQuery, profileId, requireAuthConfig, resolveAuthConfig, resolveScopes, saveOAuthState } from "../auth.js";
4
+ const authorizationEndpoint = "https://account.withings.com/oauth2_user/authorize2";
5
+ const tokenEndpoint = "https://wbsapi.withings.net/v2/oauth2";
6
+ const envPrefix = "WITHINGS";
7
+ const defaultScopes = ["user.info"];
8
+ export class Withings {
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
+ // Withings deviates from the RFC and uses a comma-delimitated string instead of spaces.
35
+ if (scopes.length > 0) {
36
+ url.searchParams.set("scope", scopes.join(","));
37
+ }
38
+ url.searchParams.set("redirect_uri", this.redirectURI);
39
+ return url;
40
+ }
41
+ async validateAuthorizationCode(code) {
42
+ const body = new URLSearchParams();
43
+ // Withings requires an `action` parameter.
44
+ body.set("action", "requesttoken");
45
+ body.set("grant_type", "authorization_code");
46
+ body.set("code", code);
47
+ body.set("redirect_uri", this.redirectURI);
48
+ body.set("client_id", this.clientId);
49
+ body.set("client_secret", this.clientSecret);
50
+ const request = createOAuth2Request(tokenEndpoint, body);
51
+ const tokens = await sendTokenRequest(request);
52
+ return tokens;
53
+ }
54
+ async getAuthorizationURL(scopes) {
55
+ const auth = requireAuthConfig(this.auth);
56
+ const state = generateOAuthState();
57
+ const url = this.createAuthorizationURL(state, resolveScopes(scopes, auth, defaultScopes));
58
+ await saveOAuthState(auth.store, state, {});
59
+ return url;
60
+ }
61
+ async getUser(query) {
62
+ const auth = requireAuthConfig(this.auth);
63
+ const { code, state } = parseCallbackQuery(query);
64
+ await consumeOAuthState(auth.store, state);
65
+ const tokens = await this.validateAuthorizationCode(code);
66
+ // Withings has no profile API; the token response only carries the user id.
67
+ const data = tokens.data;
68
+ return {
69
+ id: profileId(data.userid),
70
+ name: null,
71
+ email: null,
72
+ image: null
73
+ };
74
+ }
75
+ }
76
+ async function sendTokenRequest(request) {
77
+ let response;
78
+ try {
79
+ response = await fetch(request);
80
+ }
81
+ catch (e) {
82
+ throw new ArcticFetchError(e);
83
+ }
84
+ // Withings returns a 200 even for error responses.
85
+ if (response.status !== 200) {
86
+ if (response.body !== null) {
87
+ await response.body.cancel();
88
+ }
89
+ throw new UnexpectedResponseError(response.status);
90
+ }
91
+ let data;
92
+ try {
93
+ data = await response.json();
94
+ }
95
+ catch {
96
+ throw new UnexpectedResponseError(response.status);
97
+ }
98
+ if (typeof data !== "object" || data === null) {
99
+ throw new UnexpectedErrorResponseBodyError(response.status, data);
100
+ }
101
+ // Withings returns an `error` field but the value deviates from the RFC.
102
+ // Probably better to throw `UnexpectedErrorResponseBodyError`
103
+ // but we're keeping `OAuth2RequestError` for now to be consistent with the other providers.
104
+ if ("error" in data && typeof data.error === "string") {
105
+ let error;
106
+ try {
107
+ error = createOAuth2RequestError(data);
108
+ }
109
+ catch {
110
+ throw new UnexpectedErrorResponseBodyError(response.status, data);
111
+ }
112
+ throw error;
113
+ }
114
+ // Withings returns `{"status": 0, "body": {...}}`.
115
+ if (!("body" in data) || typeof data.body !== "object" || data.body === null) {
116
+ throw new Error("Missing or invalid 'body' field");
117
+ }
118
+ const tokens = new OAuth2Tokens(data.body);
119
+ return tokens;
120
+ }
@@ -0,0 +1,16 @@
1
+ import { type OAuth2Tokens } from "../oauth2.js";
2
+ import type { OAuthCallbackQuery, OAuthUser, ProviderOptions } from "../auth.js";
3
+ export interface WorkOSOptions extends ProviderOptions {
4
+ }
5
+ export declare class WorkOS {
6
+ private clientId;
7
+ private clientSecret;
8
+ private redirectURI;
9
+ private auth;
10
+ constructor(options: WorkOSOptions);
11
+ constructor(clientId: string, clientSecret: string | null, redirectURI: string);
12
+ createAuthorizationURL(state: string, codeVerifier: string | null): URL;
13
+ validateAuthorizationCode(code: string, codeVerifier: string | null): Promise<OAuth2Tokens>;
14
+ getAuthorizationURL(): Promise<URL>;
15
+ getUser(query: OAuthCallbackQuery): Promise<OAuthUser>;
16
+ }
@@ -0,0 +1,82 @@
1
+ import { createOAuth2Request, sendTokenRequest } from "../request.js";
2
+ import { consumeOAuthState, fetchUserProfile, generateOAuthCodeVerifier, generateOAuthState, InvalidOAuthCallbackError, parseCallbackQuery, profileId, profileString, requireAuthConfig, resolveAuthConfig, saveOAuthState } from "../auth.js";
3
+ import { createS256CodeChallenge } from "../oauth2.js";
4
+ const authorizationEndpoint = "https://api.workos.com/sso/authorize";
5
+ const tokenEndpoint = "https://api.workos.com/sso/token";
6
+ const profileEndpoint = "https://api.workos.com/sso/profile";
7
+ const envPrefix = "WORKOS";
8
+ export class WorkOS {
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, { redirectURI: 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 ?? null;
23
+ this.redirectURI = redirectURI ?? "";
24
+ }
25
+ }
26
+ createAuthorizationURL(state, codeVerifier) {
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
+ url.searchParams.set("redirect_uri", this.redirectURI);
32
+ if (codeVerifier !== null) {
33
+ const codeChallenge = createS256CodeChallenge(codeVerifier);
34
+ url.searchParams.set("code_challenge_method", "S256");
35
+ url.searchParams.set("code_challenge", codeChallenge);
36
+ }
37
+ return url;
38
+ }
39
+ async validateAuthorizationCode(code, codeVerifier) {
40
+ const body = new URLSearchParams();
41
+ body.set("grant_type", "authorization_code");
42
+ body.set("code", code);
43
+ body.set("redirect_uri", this.redirectURI);
44
+ body.set("client_id", this.clientId);
45
+ if (this.clientSecret !== null) {
46
+ body.set("client_secret", this.clientSecret);
47
+ }
48
+ if (codeVerifier !== null) {
49
+ body.set("code_verifier", codeVerifier);
50
+ }
51
+ const request = createOAuth2Request(tokenEndpoint, body);
52
+ const tokens = await sendTokenRequest(request);
53
+ return tokens;
54
+ }
55
+ async getAuthorizationURL() {
56
+ const auth = requireAuthConfig(this.auth);
57
+ const state = generateOAuthState();
58
+ const codeVerifier = generateOAuthCodeVerifier();
59
+ const url = this.createAuthorizationURL(state, codeVerifier);
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
+ const profile = await fetchUserProfile(profileEndpoint, tokens.accessToken());
72
+ const firstName = profileString(profile.first_name);
73
+ const lastName = profileString(profile.last_name);
74
+ const fullName = [firstName, lastName].filter((part) => part !== null).join(" ");
75
+ return {
76
+ id: profileId(profile.id),
77
+ name: profileString(profile.name) ?? (fullName !== "" ? fullName : null),
78
+ email: profileString(profile.email),
79
+ image: profileString(profile.profile_picture_url)
80
+ };
81
+ }
82
+ }
@@ -0,0 +1,15 @@
1
+ import type { OAuth2Tokens } from "../oauth2.js";
2
+ import type { OAuthCallbackQuery, OAuthUser, ProviderOptions } from "../auth.js";
3
+ export interface YahooOptions extends ProviderOptions {
4
+ }
5
+ export declare class Yahoo {
6
+ private client;
7
+ private auth;
8
+ constructor(options: YahooOptions);
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
+ }