arctic
Advanced tools
Comparing version 0.7.1 to 0.8.0
export { Apple } from "./providers/apple.js"; | ||
export { Atlassian } from "./providers/atlassian.js"; | ||
export { Auth0 } from "./providers/auth0.js"; | ||
export { Bitbucket } from "./providers/bitbucket.js"; | ||
export { Box } from "./providers/box.js"; | ||
export { Discord } from "./providers/discord.js"; | ||
export { Dropbox } from "./providers/dropbox.js"; | ||
export { Facebook } from "./providers/facebook.js"; | ||
@@ -13,3 +16,3 @@ export { Figma } from "./providers/figma.js"; | ||
export { LinkedIn } from "./providers/linkedin.js"; | ||
export { MicrosoftEntraID } from "./providers/microsoft-entra-id.js"; | ||
export { MicrosoftEntraId } from "./providers/microsoft-entra-id.js"; | ||
export { Notion } from "./providers/notion.js"; | ||
@@ -21,22 +24,27 @@ export { Reddit } from "./providers/reddit.js"; | ||
export { Twitter } from "./providers/twitter.js"; | ||
export type { AppleCredentials, AppleIdTokenClaims, AppleRefreshedTokens, AppleTokens } from "./providers/apple.js"; | ||
export type { Auth0Tokens, Auth0User } from "./providers/auth0.js"; | ||
export type { BitbucketLink, BitbucketLinks, BitbucketTokens, BitbucketUser } from "./providers/bitbucket.js"; | ||
export type { DiscordTokens, DiscordUser } from "./providers/discord.js"; | ||
export type { FacebookTokens, FacebookUser } from "./providers/facebook.js"; | ||
export type { FigmaRefreshedTokens, FigmaTokens, FigmaUser } from "./providers/figma.js"; | ||
export type { GitHubTokens, GitHubUser, PrivateGitHubUser, PublicGitHubUser } from "./providers/github.js"; | ||
export type { GitLabTokens, GitLabUser } from "./providers/gitlab.js"; | ||
export type { GoogleRefreshedTokens, GoogleTokens, GoogleUser } from "./providers/google.js"; | ||
export type { KakaoAccount, KakaoPartner, KakaoProfile, KakaoTokens, KakaoUser } from "./providers/kakao.js"; | ||
export type { KeycloakTokens, KeycloakUser } from "./providers/keycloak.js"; | ||
export type { LineRefreshedTokens, LineTokens, LineUser } from "./providers/line.js"; | ||
export type { LinkedInTokens, LinkedInUser } from "./providers/linkedin.js"; | ||
export type { MicrosoftEntraIDIdTokenClaims, MicrosoftEntraIDTokens, MicrosoftEntraIDUser } from "./providers/microsoft-entra-id.js"; | ||
export type { NotionPersonUser, NotionTokens, NotionUser } from "./providers/notion.js"; | ||
export type { RedditTokens, RedditUser } from "./providers/reddit.js"; | ||
export type { SlackTokens, SlackUser } from "./providers/slack.js"; | ||
export type { SpotifyTokens, SpotifyUser } from "./providers/spotify.js"; | ||
export type { TwitchTokens, TwitchUser } from "./providers/twitch.js"; | ||
export type { TwitterTokens, TwitterUser } from "./providers/twitter.js"; | ||
export { Yahoo } from "./providers/yahoo.js"; | ||
export type { AppleCredentials, AppleRefreshedTokens, AppleTokens } from "./providers/apple.js"; | ||
export type { AtlassianTokens } from "./providers/atlassian.js"; | ||
export type { Auth0Tokens } from "./providers/auth0.js"; | ||
export type { BitbucketTokens } from "./providers/bitbucket.js"; | ||
export type { BoxTokens } from "./providers/box.js"; | ||
export type { DiscordTokens } from "./providers/discord.js"; | ||
export type { DropboxRefreshedTokens, DropboxTokens } from "./providers/dropbox.js"; | ||
export type { FacebookTokens } from "./providers/facebook.js"; | ||
export type { FigmaRefreshedTokens, FigmaTokens } from "./providers/figma.js"; | ||
export type { GitHubTokens } from "./providers/github.js"; | ||
export type { GitLabTokens } from "./providers/gitlab.js"; | ||
export type { GoogleRefreshedTokens, GoogleTokens } from "./providers/google.js"; | ||
export type { KakaoTokens } from "./providers/kakao.js"; | ||
export type { KeycloakTokens } from "./providers/keycloak.js"; | ||
export type { LineRefreshedTokens, LineTokens } from "./providers/line.js"; | ||
export type { LinkedInTokens } from "./providers/linkedin.js"; | ||
export type { MicrosoftEntraIdTokens } from "./providers/microsoft-entra-id.js"; | ||
export type { NotionTokens } from "./providers/notion.js"; | ||
export type { RedditTokens } from "./providers/reddit.js"; | ||
export type { SlackTokens } from "./providers/slack.js"; | ||
export type { SpotifyTokens } from "./providers/spotify.js"; | ||
export type { TwitchTokens } from "./providers/twitch.js"; | ||
export type { TwitterTokens } from "./providers/twitter.js"; | ||
export type { YahooTokens } from "./providers/yahoo.js"; | ||
export { generateCodeVerifier, generateState, OAuth2RequestError } from "oslo/oauth2"; | ||
@@ -47,3 +55,2 @@ export interface OAuth2Provider { | ||
refreshAccessToken?(refreshToken: string): Promise<Tokens>; | ||
getUser?(accessToken: string): Promise<{}>; | ||
} | ||
@@ -54,6 +61,9 @@ export interface OAuth2ProviderWithPKCE { | ||
refreshAccessToken?(refreshToken: string): Promise<Tokens>; | ||
getUser?(accessToken: string): Promise<{}>; | ||
} | ||
export interface Tokens { | ||
accessToken: string; | ||
refreshToken?: string | null; | ||
accessTokenExpiresAt?: Date; | ||
refreshTokenExpiresAt?: Date | null; | ||
idToken?: string; | ||
} |
export { Apple } from "./providers/apple.js"; | ||
export { Atlassian } from "./providers/atlassian.js"; | ||
export { Auth0 } from "./providers/auth0.js"; | ||
export { Bitbucket } from "./providers/bitbucket.js"; | ||
export { Box } from "./providers/box.js"; | ||
export { Discord } from "./providers/discord.js"; | ||
export { Dropbox } from "./providers/dropbox.js"; | ||
export { Facebook } from "./providers/facebook.js"; | ||
@@ -13,3 +16,3 @@ export { Figma } from "./providers/figma.js"; | ||
export { LinkedIn } from "./providers/linkedin.js"; | ||
export { MicrosoftEntraID } from "./providers/microsoft-entra-id.js"; | ||
export { MicrosoftEntraId } from "./providers/microsoft-entra-id.js"; | ||
export { Notion } from "./providers/notion.js"; | ||
@@ -21,2 +24,3 @@ export { Reddit } from "./providers/reddit.js"; | ||
export { Twitter } from "./providers/twitter.js"; | ||
export { Yahoo } from "./providers/yahoo.js"; | ||
export { generateCodeVerifier, generateState, OAuth2RequestError } from "oslo/oauth2"; |
import type { OAuth2Provider } from "../index.js"; | ||
export declare class Apple implements OAuth2Provider { | ||
private client; | ||
private scope; | ||
private credentials; | ||
constructor(credentials: AppleCredentials, redirectURI: string, options?: { | ||
constructor(credentials: AppleCredentials, redirectURI: string); | ||
createAuthorizationURL(state: string, options?: { | ||
responseMode?: "query" | "form_post"; | ||
scope?: string[]; | ||
}); | ||
createAuthorizationURL(state: string): Promise<URL>; | ||
}): Promise<URL>; | ||
validateAuthorizationCode(code: string): Promise<AppleTokens>; | ||
refreshAccessToken(refreshToken: string): Promise<AppleRefreshedTokens>; | ||
private createClientSecret; | ||
private parseIdToken; | ||
} | ||
export interface AppleIdTokenClaims { | ||
iss: "https://appleid.apple.com"; | ||
sub: string; | ||
aud: string; | ||
iat: number; | ||
exp: number; | ||
email?: string; | ||
email_verified?: boolean; | ||
is_private_email?: boolean; | ||
real_user_status: 0 | 1 | 2; | ||
transfer_sub?: string; | ||
nonce?: string; | ||
nonce_supported?: boolean; | ||
} | ||
export interface AppleTokens { | ||
@@ -35,3 +19,2 @@ accessToken: string; | ||
idToken: string; | ||
idTokenClaims: AppleIdTokenClaims; | ||
} | ||
@@ -42,3 +25,2 @@ export interface AppleRefreshedTokens { | ||
idToken: string; | ||
idTokenClaims: AppleIdTokenClaims; | ||
} | ||
@@ -45,0 +27,0 @@ export interface AppleCredentials { |
import { TimeSpan, createDate } from "oslo"; | ||
import { decodeBase64 } from "oslo/encoding"; | ||
import { createJWT, parseJWT } from "oslo/jwt"; | ||
import { createJWT } from "oslo/jwt"; | ||
import { OAuth2Client } from "oslo/oauth2"; | ||
@@ -9,17 +9,16 @@ const authorizeEndpoint = "https://appleid.apple.com/auth/authorize"; | ||
client; | ||
scope; | ||
credentials; | ||
constructor(credentials, redirectURI, options) { | ||
constructor(credentials, redirectURI) { | ||
this.client = new OAuth2Client(credentials.clientId, authorizeEndpoint, tokenEndpoint, { | ||
redirectURI, | ||
responseMode: options?.responseMode | ||
redirectURI | ||
}); | ||
this.credentials = credentials; | ||
this.scope = options?.scope ?? []; | ||
} | ||
async createAuthorizationURL(state) { | ||
return await this.client.createAuthorizationURL({ | ||
async createAuthorizationURL(state, options) { | ||
const url = await this.client.createAuthorizationURL({ | ||
state, | ||
scope: this.scope | ||
scope: options?.scope ?? [] | ||
}); | ||
url.searchParams.set("response_mode", options?.responseMode ?? "query"); | ||
return url; | ||
} | ||
@@ -31,9 +30,9 @@ async validateAuthorizationCode(code) { | ||
}); | ||
return { | ||
const tokens = { | ||
accessToken: result.access_token, | ||
refreshToken: result.refresh_token ?? null, | ||
accessTokenExpiresAt: createDate(new TimeSpan(result.expires_in, "s")), | ||
idToken: result.id_token, | ||
idTokenClaims: this.parseIdToken(result.id_token) | ||
idToken: result.id_token | ||
}; | ||
return tokens; | ||
} | ||
@@ -45,8 +44,8 @@ async refreshAccessToken(refreshToken) { | ||
}); | ||
return { | ||
const tokens = { | ||
accessToken: result.access_token, | ||
accessTokenExpiresAt: createDate(new TimeSpan(result.expires_in, "s")), | ||
idToken: result.id_token, | ||
idTokenClaims: this.parseIdToken(result.id_token) | ||
idToken: result.id_token | ||
}; | ||
return tokens; | ||
} | ||
@@ -68,8 +67,2 @@ async createClientSecret() { | ||
} | ||
parseIdToken(idToken) { | ||
const parsedIdToken = parseJWT(idToken); | ||
if (!parsedIdToken) | ||
throw new Error("Failed to parse ID token"); | ||
return parsedIdToken.payload; | ||
} | ||
} | ||
@@ -76,0 +69,0 @@ function parsePKCS8PEM(pkcs8) { |
@@ -6,9 +6,7 @@ import type { OAuth2Provider } from "../index.js"; | ||
private clientSecret; | ||
private scope; | ||
constructor(appDomain: string, clientId: string, clientSecret: string, redirectURI: string, options?: { | ||
constructor(appDomain: string, clientId: string, clientSecret: string, redirectURI: string); | ||
createAuthorizationURL(state: string, options?: { | ||
scope?: string[]; | ||
}); | ||
createAuthorizationURL(state: string): Promise<URL>; | ||
}): Promise<URL>; | ||
validateAuthorizationCode(code: string): Promise<Auth0Tokens>; | ||
getUser(accessToken: string): Promise<Auth0User>; | ||
refreshAccessToken(refreshToken: string): Promise<Auth0Tokens>; | ||
@@ -21,21 +19,1 @@ } | ||
} | ||
export interface Auth0User { | ||
sub: string; | ||
name: string; | ||
picture: string; | ||
locale: string; | ||
updated_at: string; | ||
given_name?: string; | ||
family_name?: string; | ||
middle_name?: string; | ||
nickname?: string; | ||
preferred_username?: string; | ||
profile?: string; | ||
email?: string; | ||
email_verified?: boolean; | ||
gender?: string; | ||
birthdate?: string; | ||
zoneinfo?: string; | ||
phone_number?: string; | ||
phone_number_verified?: boolean; | ||
} |
@@ -6,4 +6,3 @@ import { OAuth2Client } from "oslo/oauth2"; | ||
clientSecret; | ||
scope; | ||
constructor(appDomain, clientId, clientSecret, redirectURI, options) { | ||
constructor(appDomain, clientId, clientSecret, redirectURI) { | ||
this.appDomain = appDomain; | ||
@@ -16,9 +15,9 @@ const authorizeEndpoint = this.appDomain + "/authorize"; | ||
this.clientSecret = clientSecret; | ||
this.scope = options?.scope ?? []; | ||
this.scope.push("openid", "profile"); | ||
} | ||
async createAuthorizationURL(state) { | ||
async createAuthorizationURL(state, options) { | ||
const scope = options?.scope ?? []; | ||
scope.push("openid"); | ||
return await this.client.createAuthorizationURL({ | ||
scope: this.scope, | ||
state | ||
state, | ||
scope: options?.scope ?? [] | ||
}); | ||
@@ -31,3 +30,3 @@ } | ||
}); | ||
return { | ||
const tokens = { | ||
accessToken: result.access_token, | ||
@@ -37,12 +36,4 @@ refreshToken: result.refresh_token, | ||
}; | ||
return tokens; | ||
} | ||
async getUser(accessToken) { | ||
const userinfoEndpoint = this.appDomain + "/userinfo"; | ||
const response = await fetch(userinfoEndpoint, { | ||
headers: { | ||
Authorization: `Bearer ${accessToken}` | ||
} | ||
}); | ||
return await response.json(); | ||
} | ||
async refreshAccessToken(refreshToken) { | ||
@@ -53,3 +44,3 @@ const result = await this.client.refreshAccessToken(refreshToken, { | ||
}); | ||
return { | ||
const tokens = { | ||
accessToken: result.access_token, | ||
@@ -59,3 +50,4 @@ refreshToken: result.refresh_token, | ||
}; | ||
return tokens; | ||
} | ||
} |
@@ -5,9 +5,7 @@ import type { OAuth2Provider } from "../index.js"; | ||
private clientSecret; | ||
private scope; | ||
constructor(clientId: string, clientSecret: string, redirectURI: string, options?: { | ||
constructor(clientId: string, clientSecret: string, redirectURI: string); | ||
createAuthorizationURL(state: string, options?: { | ||
scope?: string[]; | ||
}); | ||
createAuthorizationURL(state: string): Promise<URL>; | ||
}): Promise<URL>; | ||
validateAuthorizationCode(code: string): Promise<BitbucketTokens>; | ||
getUser(accessToken: string): Promise<BitbucketUser>; | ||
refreshAccessToken(refreshToken: string): Promise<BitbucketTokens>; | ||
@@ -20,16 +18,1 @@ } | ||
} | ||
export interface BitbucketUser { | ||
type: string; | ||
links: BitbucketLinks; | ||
created_on: string; | ||
display_name: string; | ||
username: string; | ||
uuid: string; | ||
} | ||
export interface BitbucketLinks { | ||
avatar: BitbucketLink; | ||
} | ||
export interface BitbucketLink { | ||
href: string; | ||
name: string; | ||
} |
@@ -5,8 +5,6 @@ import { OAuth2Client } from "oslo/oauth2"; | ||
const tokenEndpoint = "https://bitbucket.org/site/oauth2/access_token"; | ||
const userEndpoint = "https://api.bitbucket.org/2.0/user"; | ||
export class Bitbucket { | ||
client; | ||
clientSecret; | ||
scope; | ||
constructor(clientId, clientSecret, redirectURI, options) { | ||
constructor(clientId, clientSecret, redirectURI) { | ||
this.client = new OAuth2Client(clientId, authorizeEndpoint, tokenEndpoint, { | ||
@@ -16,9 +14,7 @@ redirectURI | ||
this.clientSecret = clientSecret; | ||
this.scope = options?.scope ?? []; | ||
this.scope.push("account"); | ||
} | ||
async createAuthorizationURL(state) { | ||
async createAuthorizationURL(state, options) { | ||
return await this.client.createAuthorizationURL({ | ||
scope: this.scope, | ||
state | ||
state, | ||
scope: options?.scope ?? [] | ||
}); | ||
@@ -30,3 +26,3 @@ } | ||
}); | ||
return { | ||
const tokens = { | ||
accessToken: result.access_token, | ||
@@ -36,11 +32,4 @@ accessTokenExpiresAt: createDate(new TimeSpan(result.expires_in, "s")), | ||
}; | ||
return tokens; | ||
} | ||
async getUser(accessToken) { | ||
const response = await fetch(userEndpoint, { | ||
headers: { | ||
Authorization: `Bearer ${accessToken}` | ||
} | ||
}); | ||
return await response.json(); | ||
} | ||
async refreshAccessToken(refreshToken) { | ||
@@ -50,3 +39,3 @@ const result = await this.client.refreshAccessToken(refreshToken, { | ||
}); | ||
return { | ||
const tokens = { | ||
accessToken: result.access_token, | ||
@@ -56,3 +45,4 @@ accessTokenExpiresAt: createDate(new TimeSpan(result.expires_in, "s")), | ||
}; | ||
return tokens; | ||
} | ||
} |
import type { OAuth2Provider } from "../index.js"; | ||
export declare class Discord implements OAuth2Provider { | ||
private client; | ||
private scope; | ||
private clientSecret; | ||
constructor(clientId: string, clientSecret: string, redirectURI: string, options?: { | ||
constructor(clientId: string, clientSecret: string, redirectURI: string); | ||
createAuthorizationURL(state: string, options?: { | ||
scope?: string[]; | ||
}); | ||
createAuthorizationURL(state: string): Promise<URL>; | ||
}): Promise<URL>; | ||
validateAuthorizationCode(code: string): Promise<DiscordTokens>; | ||
getUser(accessToken: string): Promise<DiscordUser>; | ||
refreshAccessToken(refreshToken: string): Promise<DiscordTokens>; | ||
@@ -19,20 +17,1 @@ } | ||
} | ||
export interface DiscordUser { | ||
id: string; | ||
username: string; | ||
discriminator: string; | ||
global_name: string | null; | ||
avatar: string | null; | ||
bot?: boolean; | ||
system?: boolean; | ||
mfa_enabled?: boolean; | ||
verified?: boolean; | ||
email?: string | null; | ||
flags?: number; | ||
banner?: string | null; | ||
accent_color?: number | null; | ||
premium_type?: number; | ||
public_flags?: number; | ||
locale?: string; | ||
avatar_decoration?: string | null; | ||
} |
@@ -0,3 +1,3 @@ | ||
import { OAuth2Client } from "oslo/oauth2"; | ||
import { TimeSpan, createDate } from "oslo"; | ||
import { OAuth2Client } from "oslo/oauth2"; | ||
const authorizeEndpoint = "https://discord.com/oauth2/authorize"; | ||
@@ -7,16 +7,13 @@ const tokenEndpoint = "https://discord.com/api/oauth2/token"; | ||
client; | ||
scope; | ||
clientSecret; | ||
constructor(clientId, clientSecret, redirectURI, options) { | ||
constructor(clientId, clientSecret, redirectURI) { | ||
this.client = new OAuth2Client(clientId, authorizeEndpoint, tokenEndpoint, { | ||
redirectURI | ||
}); | ||
this.scope = options?.scope ?? []; | ||
this.scope.push("identify"); | ||
this.clientSecret = clientSecret; | ||
} | ||
async createAuthorizationURL(state) { | ||
async createAuthorizationURL(state, options) { | ||
return await this.client.createAuthorizationURL({ | ||
state, | ||
scope: this.scope | ||
scope: options?.scope ?? [] | ||
}); | ||
@@ -29,3 +26,3 @@ } | ||
}); | ||
return { | ||
const tokens = { | ||
accessToken: result.access_token, | ||
@@ -35,11 +32,4 @@ refreshToken: result.refresh_token, | ||
}; | ||
return tokens; | ||
} | ||
async getUser(accessToken) { | ||
const response = await fetch("https://discord.com/api/users/@me", { | ||
headers: { | ||
Authorization: `Bearer ${accessToken}` | ||
} | ||
}); | ||
return await response.json(); | ||
} | ||
async refreshAccessToken(refreshToken) { | ||
@@ -50,3 +40,3 @@ const result = await this.client.refreshAccessToken(refreshToken, { | ||
}); | ||
return { | ||
const tokens = { | ||
accessToken: result.access_token, | ||
@@ -56,3 +46,4 @@ refreshToken: result.refresh_token, | ||
}; | ||
return tokens; | ||
} | ||
} |
import type { OAuth2Provider } from "../index.js"; | ||
export declare class Facebook implements OAuth2Provider { | ||
private client; | ||
private scope; | ||
private clientSecret; | ||
constructor(clientId: string, clientSecret: string, redirectURI: string, options?: { | ||
constructor(clientId: string, clientSecret: string, redirectURI: string); | ||
createAuthorizationURL(state: string, options?: { | ||
scope?: string[]; | ||
}); | ||
createAuthorizationURL(state: string): Promise<URL>; | ||
}): Promise<URL>; | ||
validateAuthorizationCode(code: string): Promise<FacebookTokens>; | ||
getUser(accessToken: string): Promise<FacebookUser>; | ||
} | ||
@@ -17,14 +15,1 @@ export interface FacebookTokens { | ||
} | ||
export interface FacebookUser { | ||
id: string; | ||
name: string; | ||
email?: string; | ||
picture: { | ||
data: { | ||
height: number; | ||
is_silhouette: boolean; | ||
url: string; | ||
width: number; | ||
}; | ||
}; | ||
} |
@@ -7,15 +7,13 @@ import { OAuth2Client } from "oslo/oauth2"; | ||
client; | ||
scope; | ||
clientSecret; | ||
constructor(clientId, clientSecret, redirectURI, options) { | ||
constructor(clientId, clientSecret, redirectURI) { | ||
this.client = new OAuth2Client(clientId, authorizeEndpoint, tokenEndpoint, { | ||
redirectURI | ||
}); | ||
this.scope = options?.scope ?? []; | ||
this.clientSecret = clientSecret; | ||
} | ||
async createAuthorizationURL(state) { | ||
async createAuthorizationURL(state, options) { | ||
return await this.client.createAuthorizationURL({ | ||
state, | ||
scope: this.scope | ||
scope: options?.scope | ||
}); | ||
@@ -28,18 +26,8 @@ } | ||
}); | ||
return { | ||
const tokens = { | ||
accessToken: result.access_token, | ||
accessTokenExpiresAt: createDate(new TimeSpan(result.expires_in, "s")) | ||
}; | ||
return tokens; | ||
} | ||
async getUser(accessToken) { | ||
const url = new URL("https://graph.facebook.com/me"); | ||
url.searchParams.set("access_token", accessToken); | ||
url.searchParams.set("fields", ["id", "name", "picture", "email"].join(",")); | ||
const response = await fetch(url, { | ||
headers: { | ||
Authorization: `Bearer ${accessToken}` | ||
} | ||
}); | ||
return await response.json(); | ||
} | ||
} |
import type { OAuth2Provider } from "../index.js"; | ||
export declare class Figma implements OAuth2Provider { | ||
private client; | ||
private scope; | ||
private clientSecret; | ||
constructor(clientId: string, clientSecret: string, redirectURI: string, options?: { | ||
constructor(clientId: string, clientSecret: string, redirectURI: string); | ||
createAuthorizationURL(state: string, options?: { | ||
scope?: string[]; | ||
}); | ||
createAuthorizationURL(state: string): Promise<URL>; | ||
}): Promise<URL>; | ||
validateAuthorizationCode(code: string): Promise<FigmaTokens>; | ||
getUser(accessToken: string): Promise<FigmaUser>; | ||
refreshAccessToken(refreshToken: string): Promise<FigmaRefreshedTokens>; | ||
@@ -24,7 +22,1 @@ } | ||
} | ||
export interface FigmaUser { | ||
id: string; | ||
handle: string; | ||
img_url: string; | ||
email: string; | ||
} |
import { TimeSpan, createDate } from "oslo"; | ||
import { OAuth2Client } from "oslo/oauth2"; | ||
import { sendRequest } from "../request.js"; | ||
const authorizeEndpoint = "https://www.figma.com/oauth"; | ||
const tokenEndpoint = "https://www.figma.com/api/oauth/token"; | ||
const userEndpoint = "https://api.figma.com/v1/me"; | ||
export class Figma { | ||
client; | ||
scope; | ||
clientSecret; | ||
constructor(clientId, clientSecret, redirectURI, options) { | ||
constructor(clientId, clientSecret, redirectURI) { | ||
this.client = new OAuth2Client(clientId, authorizeEndpoint, tokenEndpoint, { | ||
redirectURI | ||
}); | ||
this.scope = options?.scope ?? []; | ||
this.clientSecret = clientSecret; | ||
} | ||
async createAuthorizationURL(state) { | ||
async createAuthorizationURL(state, options) { | ||
const url = await this.client.createAuthorizationURL({ | ||
scope: this.scope, | ||
state | ||
state, | ||
scope: options?.scope ?? [] | ||
}); | ||
@@ -30,3 +26,3 @@ return url; | ||
}); | ||
return { | ||
const tokens = { | ||
accessToken: result.access_token, | ||
@@ -37,8 +33,4 @@ refreshToken: result.refresh_token ?? null, | ||
}; | ||
return tokens; | ||
} | ||
async getUser(accessToken) { | ||
const request = new Request(userEndpoint); | ||
request.headers.set("Authorization", `Bearer ${accessToken}`); | ||
return await sendRequest(request); | ||
} | ||
async refreshAccessToken(refreshToken) { | ||
@@ -49,7 +41,8 @@ const result = await this.client.refreshAccessToken(refreshToken, { | ||
}); | ||
return { | ||
const tokens = { | ||
accessToken: result.access_token, | ||
accessTokenExpiresAt: createDate(new TimeSpan(result.expires_in, "s")) | ||
}; | ||
return tokens; | ||
} | ||
} |
import type { OAuth2Provider } from "../index.js"; | ||
export declare class GitHub implements OAuth2Provider { | ||
private client; | ||
private scope; | ||
private clientSecret; | ||
constructor(clientId: string, clientSecret: string, options?: { | ||
redirectURI?: string; | ||
}); | ||
createAuthorizationURL(state: string, options?: { | ||
scope?: string[]; | ||
}); | ||
createAuthorizationURL(state: string): Promise<URL>; | ||
}): Promise<URL>; | ||
validateAuthorizationCode(code: string): Promise<GitHubTokens>; | ||
getUser(accessToken: string): Promise<GitHubUser>; | ||
} | ||
@@ -17,53 +16,1 @@ export interface GitHubTokens { | ||
} | ||
export type GitHubUser = PublicGitHubUser | PrivateGitHubUser; | ||
export interface PublicGitHubUser { | ||
avatar_url: string; | ||
bio: string | null; | ||
blog: string | null; | ||
company: string | null; | ||
created_at: string; | ||
email: string | null; | ||
events_url: string; | ||
followers: number; | ||
followers_url: string; | ||
following: number; | ||
following_url: string; | ||
gists_url: string; | ||
gravatar_id: string | null; | ||
hireable: boolean | null; | ||
html_url: string; | ||
id: number; | ||
location: string | null; | ||
login: string; | ||
name: string | null; | ||
node_id: string; | ||
organizations_url: string; | ||
public_gists: number; | ||
public_repos: number; | ||
received_events_url: string; | ||
repos_url: string; | ||
site_admin: boolean; | ||
starred_url: string; | ||
subscriptions_url: string; | ||
type: string; | ||
updated_at: string; | ||
url: string; | ||
twitter_username?: string | null; | ||
plan?: { | ||
name: string; | ||
space: number; | ||
private_repos: number; | ||
collaborators: number; | ||
}; | ||
suspended_at?: string | null; | ||
} | ||
export interface PrivateGitHubUser extends PublicGitHubUser { | ||
collaborators: number; | ||
disk_usage: number; | ||
owned_private_repos: number; | ||
private_gists: number; | ||
total_private_repos: number; | ||
two_factor_authentication: boolean; | ||
business_plus?: boolean; | ||
ldap_dn?: string; | ||
} |
@@ -6,3 +6,2 @@ import { OAuth2Client } from "oslo/oauth2"; | ||
client; | ||
scope; | ||
clientSecret; | ||
@@ -13,9 +12,8 @@ constructor(clientId, clientSecret, options) { | ||
}); | ||
this.scope = options?.scope ?? []; | ||
this.clientSecret = clientSecret; | ||
} | ||
async createAuthorizationURL(state) { | ||
async createAuthorizationURL(state, options) { | ||
return await this.client.createAuthorizationURL({ | ||
state, | ||
scope: this.scope | ||
scope: options?.scope ?? [] | ||
}); | ||
@@ -28,14 +26,7 @@ } | ||
}); | ||
return { | ||
const tokens = { | ||
accessToken: result.access_token | ||
}; | ||
return tokens; | ||
} | ||
async getUser(accessToken) { | ||
const response = await fetch("https://api.github.com/user", { | ||
headers: { | ||
Authorization: `Bearer ${accessToken}` | ||
} | ||
}); | ||
return await response.json(); | ||
} | ||
} |
@@ -5,11 +5,9 @@ import type { OAuth2Provider } from "../index.js"; | ||
private clientSecret; | ||
private scope; | ||
private domain; | ||
constructor(clientId: string, clientSecret: string, redirectURI: string, options?: { | ||
domain?: string; | ||
}); | ||
createAuthorizationURL(state: string, options?: { | ||
scope?: string[]; | ||
}); | ||
createAuthorizationURL(state: string): Promise<URL>; | ||
}): Promise<URL>; | ||
validateAuthorizationCode(code: string): Promise<GitLabTokens>; | ||
getUser(accessToken: string): Promise<GitLabUser>; | ||
refreshAccessToken(refreshToken: string): Promise<GitLabTokens>; | ||
@@ -22,43 +20,1 @@ } | ||
} | ||
export interface GitLabUser { | ||
id: number; | ||
username: string; | ||
email: string; | ||
name: string; | ||
state: string; | ||
avatar_url: string; | ||
web_url: string; | ||
created_at: string; | ||
bio: string; | ||
public_email: string; | ||
skype: string; | ||
linkedin: string; | ||
twitter: string; | ||
discord: string; | ||
website_url: string; | ||
organization: string; | ||
job_title: string; | ||
pronouns: string; | ||
bot: boolean; | ||
work_information: string | null; | ||
followers: number; | ||
following: number; | ||
local_time: string; | ||
last_sign_in_at: string; | ||
confirmed_at: string; | ||
theme_id: number; | ||
last_activity_on: string; | ||
color_scheme_id: number; | ||
projects_limit: number; | ||
current_sign_in_at: string; | ||
identities: { | ||
provider: string; | ||
extern_uid: string; | ||
}[]; | ||
can_create_group: boolean; | ||
can_create_project: boolean; | ||
two_factor_enabled: boolean; | ||
external: boolean; | ||
private_profile: boolean; | ||
commit_email: string; | ||
} |
@@ -6,8 +6,6 @@ import { OAuth2Client } from "oslo/oauth2"; | ||
clientSecret; | ||
scope; | ||
domain; | ||
constructor(clientId, clientSecret, redirectURI, options) { | ||
this.domain = options?.domain ?? "https://gitlab.com"; | ||
const authorizeEndpoint = this.domain + "/oauth/authorize"; | ||
const tokenEndpoint = this.domain + "/oauth/token"; | ||
const domain = options?.domain ?? "https://gitlab.com"; | ||
const authorizeEndpoint = domain + "/oauth/authorize"; | ||
const tokenEndpoint = domain + "/oauth/token"; | ||
this.client = new OAuth2Client(clientId, authorizeEndpoint, tokenEndpoint, { | ||
@@ -17,9 +15,7 @@ redirectURI | ||
this.clientSecret = clientSecret; | ||
this.scope = options?.scope ?? []; | ||
this.scope.push("read_user"); | ||
} | ||
async createAuthorizationURL(state) { | ||
async createAuthorizationURL(state, options) { | ||
return await this.client.createAuthorizationURL({ | ||
scope: this.scope, | ||
state | ||
state, | ||
scope: options?.scope ?? [] | ||
}); | ||
@@ -32,3 +28,3 @@ } | ||
}); | ||
return { | ||
const tokens = { | ||
accessToken: result.access_token, | ||
@@ -38,11 +34,4 @@ accessTokenExpiresAt: createDate(new TimeSpan(result.expires_in, "s")), | ||
}; | ||
return tokens; | ||
} | ||
async getUser(accessToken) { | ||
const response = await fetch(this.domain + "/api/v4/user", { | ||
headers: { | ||
Authorization: `Bearer ${accessToken}` | ||
} | ||
}); | ||
return await response.json(); | ||
} | ||
async refreshAccessToken(refreshToken) { | ||
@@ -53,3 +42,3 @@ const result = await this.client.refreshAccessToken(refreshToken, { | ||
}); | ||
return { | ||
const tokens = { | ||
accessToken: result.access_token, | ||
@@ -59,3 +48,4 @@ accessTokenExpiresAt: createDate(new TimeSpan(result.expires_in, "s")), | ||
}; | ||
return tokens; | ||
} | ||
} |
import type { OAuth2ProviderWithPKCE } from "../index.js"; | ||
export declare class Google implements OAuth2ProviderWithPKCE { | ||
private client; | ||
private scope; | ||
private clientSecret; | ||
private accessType; | ||
constructor(clientId: string, clientSecret: string, redirectURI: string, options?: { | ||
constructor(clientId: string, clientSecret: string, redirectURI: string); | ||
createAuthorizationURL(codeVerifier: string, options?: { | ||
scope?: string[]; | ||
accessType?: "online" | "offline"; | ||
}); | ||
createAuthorizationURL(codeVerifier: string): Promise<URL>; | ||
}): Promise<URL>; | ||
validateAuthorizationCode(code: string, codeVerifier: string): Promise<GoogleTokens>; | ||
getUser(accessToken: string): Promise<GoogleUser>; | ||
refreshAccessToken(refreshToken: string): Promise<GoogleRefreshedTokens>; | ||
@@ -26,12 +23,1 @@ } | ||
} | ||
export interface GoogleUser { | ||
sub: string; | ||
name: string; | ||
given_name: string; | ||
family_name: string; | ||
picture: string; | ||
locale: string; | ||
email?: string; | ||
email_verified?: boolean; | ||
hd?: string; | ||
} |
import { TimeSpan, createDate } from "oslo"; | ||
import { OAuth2Client } from "oslo/oauth2"; | ||
import { sendRequest } from "../request.js"; | ||
const authorizeEndpoint = "https://accounts.google.com/o/oauth2/v2/auth"; | ||
const tokenEndpoint = "https://oauth2.googleapis.com/token"; | ||
const userinfoEndpoint = "https://openidconnect.googleapis.com/v1/userinfo"; | ||
export class Google { | ||
client; | ||
scope; | ||
clientSecret; | ||
accessType; | ||
constructor(clientId, clientSecret, redirectURI, options) { | ||
constructor(clientId, clientSecret, redirectURI) { | ||
this.client = new OAuth2Client(clientId, authorizeEndpoint, tokenEndpoint, { | ||
redirectURI | ||
}); | ||
this.scope = options?.scope ?? []; | ||
this.scope.push("openid", "profile"); | ||
this.clientSecret = clientSecret; | ||
this.accessType = options?.accessType ?? "online"; | ||
} | ||
async createAuthorizationURL(codeVerifier) { | ||
async createAuthorizationURL(codeVerifier, options) { | ||
const scope = options?.scope ?? []; | ||
scope.push("openid"); | ||
const url = await this.client.createAuthorizationURL({ | ||
scope: this.scope, | ||
codeVerifier | ||
codeVerifier, | ||
scope | ||
}); | ||
url.searchParams.set("access_type", this.accessType); | ||
url.searchParams.set("access_type", options?.accessType ?? "online"); | ||
return url; | ||
@@ -35,3 +30,3 @@ } | ||
}); | ||
return { | ||
const tokens = { | ||
accessToken: result.access_token, | ||
@@ -42,8 +37,4 @@ refreshToken: result.refresh_token ?? null, | ||
}; | ||
return tokens; | ||
} | ||
async getUser(accessToken) { | ||
const request = new Request(userinfoEndpoint); | ||
request.headers.set("Authorization", `Bearer ${accessToken}`); | ||
return await sendRequest(request); | ||
} | ||
async refreshAccessToken(refreshToken) { | ||
@@ -54,7 +45,8 @@ const result = await this.client.refreshAccessToken(refreshToken, { | ||
}); | ||
return { | ||
const tokens = { | ||
accessToken: result.access_token, | ||
accessTokenExpiresAt: createDate(new TimeSpan(result.expires_in, "s")) | ||
}; | ||
return tokens; | ||
} | ||
} |
@@ -1,12 +0,10 @@ | ||
import type { OAuth2ProviderWithPKCE } from "../index.js"; | ||
export declare class Kakao implements OAuth2ProviderWithPKCE { | ||
import type { OAuth2Provider } from "../index.js"; | ||
export declare class Kakao implements OAuth2Provider { | ||
private client; | ||
private scope; | ||
private clientSecret; | ||
constructor(clientId: string, clientSecret: string, redirectURI: string, options?: { | ||
constructor(clientId: string, clientSecret: string, redirectURI: string); | ||
createAuthorizationURL(state: string, options?: { | ||
scope?: string[]; | ||
}); | ||
createAuthorizationURL(codeVerifier: string): Promise<URL>; | ||
validateAuthorizationCode(code: string, codeVerifier: string): Promise<KakaoTokens>; | ||
getUser(accessToken: string): Promise<KakaoUser>; | ||
}): Promise<URL>; | ||
validateAuthorizationCode(code: string): Promise<KakaoTokens>; | ||
refreshAccessToken(refreshToken: string): Promise<KakaoTokens>; | ||
@@ -20,45 +18,1 @@ } | ||
} | ||
export interface KakaoUser { | ||
id: number; | ||
has_signed_up?: boolean; | ||
connected_at?: string; | ||
synced_at?: string; | ||
properties?: Record<string, string>; | ||
kakao_account?: KakaoAccount; | ||
for_partner?: KakaoPartner; | ||
} | ||
export interface KakaoAccount { | ||
profile_needs_agreement?: boolean; | ||
profile_nickname_needs_agreement?: boolean; | ||
profile_image_needs_agreement?: boolean; | ||
profile?: KakaoProfile; | ||
email_needs_agreement?: boolean; | ||
is_email_valid?: boolean; | ||
is_email_verified?: boolean; | ||
email?: string; | ||
name_needs_agreement?: boolean; | ||
name?: string; | ||
age_range_needs_agreement?: boolean; | ||
ag_range?: "1~9" | "10~14" | "15~19" | "20~29" | "30~39" | "40~49" | "50~59" | "60~69" | "70~79" | "80~89" | "90~"; | ||
birthyear_needs_agreement?: boolean; | ||
birthyear?: string; | ||
birthday_needs_agreement?: boolean; | ||
birthday?: string; | ||
birthday_type?: "SOLAR" | "LUNAR"; | ||
gender_needs_agreement?: boolean; | ||
gender?: "female" | "male"; | ||
phone_number_needs_agreement?: boolean; | ||
phone_number?: string; | ||
ci_needs_agreement?: boolean; | ||
ci?: string; | ||
ci_authenticated_at?: string; | ||
} | ||
export interface KakaoProfile { | ||
nickname?: string; | ||
thumbnail_image_url?: string; | ||
profile_image_url?: string; | ||
is_default_image?: boolean; | ||
} | ||
export interface KakaoPartner { | ||
uuid?: string; | ||
} |
@@ -7,24 +7,21 @@ import { TimeSpan, createDate } from "oslo"; | ||
client; | ||
scope; | ||
clientSecret; | ||
constructor(clientId, clientSecret, redirectURI, options) { | ||
constructor(clientId, clientSecret, redirectURI) { | ||
this.client = new OAuth2Client(clientId, authorizeEndpoint, tokenEndpoint, { | ||
redirectURI | ||
}); | ||
this.scope = options?.scope ?? []; | ||
this.clientSecret = clientSecret; | ||
} | ||
async createAuthorizationURL(codeVerifier) { | ||
async createAuthorizationURL(state, options) { | ||
return await this.client.createAuthorizationURL({ | ||
codeVerifier, | ||
scope: this.scope | ||
state, | ||
scope: options?.scope ?? [] | ||
}); | ||
} | ||
async validateAuthorizationCode(code, codeVerifier) { | ||
async validateAuthorizationCode(code) { | ||
const result = await this.client.validateAuthorizationCode(code, { | ||
authenticateWith: "request_body", | ||
credentials: this.clientSecret, | ||
codeVerifier | ||
credentials: this.clientSecret | ||
}); | ||
return { | ||
const tokens = { | ||
accessToken: result.access_token, | ||
@@ -35,11 +32,4 @@ accessTokenExpiresAt: createDate(new TimeSpan(result.expires_in, "s")), | ||
}; | ||
return tokens; | ||
} | ||
async getUser(accessToken) { | ||
const response = await fetch("https://kapi.kakao.com/v2/user/me", { | ||
headers: { | ||
Authorization: `Bearer ${accessToken}` | ||
} | ||
}); | ||
return await response.json(); | ||
} | ||
async refreshAccessToken(refreshToken) { | ||
@@ -50,3 +40,3 @@ const result = await this.client.refreshAccessToken(refreshToken, { | ||
}); | ||
return { | ||
const tokens = { | ||
accessToken: result.access_token, | ||
@@ -57,3 +47,4 @@ accessTokenExpiresAt: createDate(new TimeSpan(result.expires_in, "s")), | ||
}; | ||
return tokens; | ||
} | ||
} |
@@ -6,9 +6,7 @@ import type { OAuth2ProviderWithPKCE } from "../index.js"; | ||
private clientSecret; | ||
private scope; | ||
constructor(realmURL: string, clientId: string, clientSecret: string, redirectURI: string, options?: { | ||
constructor(realmURL: string, clientId: string, clientSecret: string, redirectURI: string); | ||
createAuthorizationURL(codeVerifier: string, options?: { | ||
scope?: string[]; | ||
}); | ||
createAuthorizationURL(codeVerifier: string): Promise<URL>; | ||
}): Promise<URL>; | ||
validateAuthorizationCode(code: string, codeVerifier: string): Promise<KeycloakTokens>; | ||
getUser(accessToken: string): Promise<KeycloakUser>; | ||
refreshAccessToken(refreshToken: string): Promise<KeycloakTokens>; | ||
@@ -23,25 +21,1 @@ } | ||
} | ||
export interface KeycloakUser { | ||
exp: number; | ||
iat: number; | ||
auth_time: number; | ||
jti: string; | ||
iss: string; | ||
aud: string; | ||
sub: string; | ||
typ: string; | ||
azp: string; | ||
session_state: string; | ||
at_hash: string; | ||
acr: string; | ||
sid: string; | ||
email_verified: boolean; | ||
name: string; | ||
preferred_username: string; | ||
given_name: string; | ||
locale: string; | ||
family_name: string; | ||
email: string; | ||
picture: string; | ||
user: any; | ||
} |
@@ -7,4 +7,3 @@ import { OAuth2Client } from "oslo/oauth2"; | ||
clientSecret; | ||
scope; | ||
constructor(realmURL, clientId, clientSecret, redirectURI, options) { | ||
constructor(realmURL, clientId, clientSecret, redirectURI) { | ||
this.realmURL = realmURL; | ||
@@ -17,9 +16,9 @@ const authorizeEndpoint = this.realmURL + "/protocol/openid-connect/auth"; | ||
this.clientSecret = clientSecret; | ||
this.scope = options?.scope ?? []; | ||
this.scope.push("openid", "profile"); | ||
} | ||
async createAuthorizationURL(codeVerifier) { | ||
async createAuthorizationURL(codeVerifier, options) { | ||
const scope = options?.scope ?? []; | ||
scope.push("openid"); | ||
return await this.client.createAuthorizationURL({ | ||
scope: this.scope, | ||
codeVerifier | ||
codeVerifier, | ||
scope | ||
}); | ||
@@ -33,3 +32,3 @@ } | ||
}); | ||
return { | ||
const tokens = { | ||
accessToken: result.access_token, | ||
@@ -41,12 +40,4 @@ accessTokenExpiresAt: createDate(new TimeSpan(result.expires_in, "s")), | ||
}; | ||
return tokens; | ||
} | ||
async getUser(accessToken) { | ||
const userinfoEndpoint = this.realmURL + "/protocol/openid-connect/userinfo"; | ||
const response = await fetch(userinfoEndpoint, { | ||
headers: { | ||
Authorization: `Bearer ${accessToken}` | ||
} | ||
}); | ||
return await response.json(); | ||
} | ||
async refreshAccessToken(refreshToken) { | ||
@@ -57,3 +48,3 @@ const result = await this.client.refreshAccessToken(refreshToken, { | ||
}); | ||
return { | ||
const tokens = { | ||
accessToken: result.access_token, | ||
@@ -65,3 +56,4 @@ accessTokenExpiresAt: createDate(new TimeSpan(result.expires_in, "s")), | ||
}; | ||
return tokens; | ||
} | ||
} |
import type { OAuth2ProviderWithPKCE } from "../index.js"; | ||
export declare class Line implements OAuth2ProviderWithPKCE { | ||
private client; | ||
private scope; | ||
private clientSecret; | ||
constructor(clientId: string, clientSecret: string, redirectURI: string, options?: { | ||
constructor(clientId: string, clientSecret: string, redirectURI: string); | ||
createAuthorizationURL(codeVerifier: string, options?: { | ||
scope?: string[]; | ||
}); | ||
createAuthorizationURL(codeVerifier: string): Promise<URL>; | ||
}): Promise<URL>; | ||
validateAuthorizationCode(code: string, codeVerifier: string): Promise<LineTokens>; | ||
getUser(accessToken: string): Promise<LineUser>; | ||
refreshAccessToken(refreshToken: string): Promise<LineRefreshedTokens>; | ||
@@ -25,6 +23,1 @@ } | ||
} | ||
export interface LineUser { | ||
sub: string; | ||
name: string; | ||
picture: string; | ||
} |
@@ -7,17 +7,16 @@ import { OAuth2Client, generateState } from "oslo/oauth2"; | ||
client; | ||
scope; | ||
clientSecret; | ||
constructor(clientId, clientSecret, redirectURI, options) { | ||
constructor(clientId, clientSecret, redirectURI) { | ||
this.client = new OAuth2Client(clientId, authorizeEndpoint, tokenEndpoint, { | ||
redirectURI | ||
}); | ||
this.scope = options?.scope ?? []; | ||
this.scope.push("profile", "openid"); | ||
this.clientSecret = clientSecret; | ||
} | ||
async createAuthorizationURL(codeVerifier) { | ||
async createAuthorizationURL(codeVerifier, options) { | ||
const scope = options?.scope ?? []; | ||
scope.push("openid"); | ||
return await this.client.createAuthorizationURL({ | ||
state: generateState(), | ||
codeVerifier, | ||
scope: this.scope | ||
scope | ||
}); | ||
@@ -31,3 +30,3 @@ } | ||
}); | ||
return { | ||
const tokens = { | ||
accessToken: result.access_token, | ||
@@ -38,11 +37,4 @@ refreshToken: result.refresh_token, | ||
}; | ||
return tokens; | ||
} | ||
async getUser(accessToken) { | ||
const response = await fetch("https://api.line.me/v2/profile", { | ||
headers: { | ||
Authorization: `Bearer ${accessToken}` | ||
} | ||
}); | ||
return await response.json(); | ||
} | ||
async refreshAccessToken(refreshToken) { | ||
@@ -53,3 +45,3 @@ const result = await this.client.refreshAccessToken(refreshToken, { | ||
}); | ||
return { | ||
const tokens = { | ||
accessToken: result.access_token, | ||
@@ -59,3 +51,4 @@ refreshToken: result.refresh_token, | ||
}; | ||
return tokens; | ||
} | ||
} |
@@ -5,9 +5,8 @@ import type { OAuth2Provider } from "../index.js"; | ||
private clientSecret; | ||
private scope; | ||
constructor(clientId: string, clientSecret: string, redirectURI: string, options?: { | ||
constructor(clientId: string, clientSecret: string, redirectURI: string); | ||
createAuthorizationURL(state: string, options?: { | ||
scope?: string[]; | ||
}); | ||
createAuthorizationURL(state: string): Promise<URL>; | ||
}): Promise<URL>; | ||
validateAuthorizationCode(code: string): Promise<LinkedInTokens>; | ||
getUser(accessToken: string): Promise<LinkedInUser>; | ||
refreshAccessToken(accessToken: string): Promise<LinkedInTokens>; | ||
} | ||
@@ -20,14 +19,1 @@ export interface LinkedInTokens { | ||
} | ||
export interface LinkedInUser { | ||
sub: string; | ||
name: string; | ||
email: string; | ||
email_verified: boolean; | ||
given_name: string; | ||
family_name: string; | ||
locale: { | ||
country: string; | ||
language: string; | ||
}; | ||
picture: string; | ||
} |
@@ -5,8 +5,6 @@ import { OAuth2Client } from "oslo/oauth2"; | ||
const tokenEndpoint = "https://www.linkedin.com/oauth/v2/accessToken"; | ||
const userinfoEndpoint = "https://api.linkedin.com/v2/userinfo"; | ||
export class LinkedIn { | ||
client; | ||
clientSecret; | ||
scope; | ||
constructor(clientId, clientSecret, redirectURI, options) { | ||
constructor(clientId, clientSecret, redirectURI) { | ||
this.client = new OAuth2Client(clientId, authorizeEndpoint, tokenEndpoint, { | ||
@@ -16,9 +14,9 @@ redirectURI | ||
this.clientSecret = clientSecret; | ||
this.scope = options?.scope ?? []; | ||
this.scope.push("openid", "profile"); | ||
} | ||
async createAuthorizationURL(state) { | ||
async createAuthorizationURL(state, options) { | ||
const scope = options?.scope ?? []; | ||
scope.push("openid"); | ||
return await this.client.createAuthorizationURL({ | ||
state, | ||
scope: this.scope | ||
scope | ||
}); | ||
@@ -31,3 +29,3 @@ } | ||
}); | ||
return { | ||
const tokens = { | ||
accessToken: result.access_token, | ||
@@ -38,11 +36,17 @@ accessTokenExpiresAt: createDate(new TimeSpan(result.expires_in, "s")), | ||
}; | ||
return tokens; | ||
} | ||
async getUser(accessToken) { | ||
const response = await fetch(userinfoEndpoint, { | ||
headers: { | ||
Authorization: `Bearer ${accessToken}` | ||
} | ||
async refreshAccessToken(accessToken) { | ||
const result = await this.client.refreshAccessToken(accessToken, { | ||
authenticateWith: "request_body", | ||
credentials: this.clientSecret | ||
}); | ||
return await response.json(); | ||
const tokens = { | ||
accessToken: result.access_token, | ||
accessTokenExpiresAt: createDate(new TimeSpan(result.expires_in, "s")), | ||
refreshToken: result.refresh_token, | ||
refreshTokenExpiresAt: createDate(new TimeSpan(result.refresh_token_expires_in, "s")) | ||
}; | ||
return tokens; | ||
} | ||
} |
import type { OAuth2ProviderWithPKCE } from "../index.js"; | ||
export declare class MicrosoftEntraID implements OAuth2ProviderWithPKCE { | ||
export declare class MicrosoftEntraId implements OAuth2ProviderWithPKCE { | ||
private client; | ||
private scope; | ||
private clientSecret; | ||
constructor(tenant: string, clientId: string, clientSecret: string, redirectURI: string, options?: { | ||
constructor(tenant: string, clientId: string, clientSecret: string, redirectURI: string); | ||
createAuthorizationURL(codeVerifier: string, options?: { | ||
scope?: string[]; | ||
}); | ||
createAuthorizationURL(codeVerifier: string): Promise<URL>; | ||
validateAuthorizationCode(code: string, codeVerifier: string): Promise<MicrosoftEntraIDTokens>; | ||
getUser(accessToken: string): Promise<MicrosoftEntraIDUser>; | ||
refreshAccessToken(refreshToken: string): Promise<MicrosoftEntraIDTokens>; | ||
private parseIdToken; | ||
}): Promise<URL>; | ||
validateAuthorizationCode(code: string, codeVerifier: string): Promise<MicrosoftEntraIdTokens>; | ||
refreshAccessToken(refreshToken: string): Promise<MicrosoftEntraIdTokens>; | ||
} | ||
export interface MicrosoftEntraIDIdTokenClaims { | ||
sub: string; | ||
iss: string; | ||
aud: string; | ||
idp: string; | ||
iat: number; | ||
nbf: number; | ||
exp: number; | ||
at_hash: string; | ||
preferred_username: string; | ||
email?: string; | ||
name: string; | ||
oid: string; | ||
roles: string[]; | ||
tid: string; | ||
uti: string; | ||
ver: "1.0" | "2.0"; | ||
hasgroups?: true; | ||
} | ||
export interface MicrosoftEntraIDTokens { | ||
export interface MicrosoftEntraIdTokens { | ||
idToken: string; | ||
@@ -39,11 +17,2 @@ accessToken: string; | ||
refreshToken: string | null; | ||
idTokenClaims: MicrosoftEntraIDIdTokenClaims; | ||
} | ||
export interface MicrosoftEntraIDUser { | ||
sub: string; | ||
name: string; | ||
family_name: string; | ||
given_name: string; | ||
picture: string; | ||
email?: string; | ||
} |
import { TimeSpan, createDate } from "oslo"; | ||
import { parseJWT } from "oslo/jwt"; | ||
import { OAuth2Client } from "oslo/oauth2"; | ||
export class MicrosoftEntraID { | ||
export class MicrosoftEntraId { | ||
client; | ||
scope; | ||
clientSecret; | ||
constructor(tenant, clientId, clientSecret, redirectURI, options) { | ||
constructor(tenant, clientId, clientSecret, redirectURI) { | ||
const authorizeEndpoint = `https://login.microsoftonline.com/${tenant}/oauth2/v2.0/authorize`; | ||
@@ -14,10 +12,10 @@ const tokenEndpoint = `https://login.microsoftonline.com/${tenant}/oauth2/v2.0/token`; | ||
}); | ||
this.scope = options?.scope ?? []; | ||
this.scope.push("openid", "profile"); | ||
this.clientSecret = clientSecret; | ||
} | ||
async createAuthorizationURL(codeVerifier) { | ||
async createAuthorizationURL(codeVerifier, options) { | ||
const scope = options?.scope ?? []; | ||
scope.push("openid"); | ||
const url = await this.client.createAuthorizationURL({ | ||
scope: this.scope, | ||
codeVerifier | ||
codeVerifier, | ||
scope | ||
}); | ||
@@ -33,18 +31,10 @@ url.searchParams.set("nonce", "_"); | ||
}); | ||
return { | ||
const tokens = { | ||
accessToken: result.access_token, | ||
refreshToken: result.refresh_token ?? null, | ||
accessTokenExpiresAt: createDate(new TimeSpan(result.expires_in, "s")), | ||
idToken: result.id_token, | ||
idTokenClaims: this.parseIdToken(result.id_token) | ||
idToken: result.id_token | ||
}; | ||
return tokens; | ||
} | ||
async getUser(accessToken) { | ||
const response = await fetch("https://graph.microsoft.com/oidc/userinfo", { | ||
headers: { | ||
Authorization: `Bearer ${accessToken}` | ||
} | ||
}); | ||
return await response.json(); | ||
} | ||
async refreshAccessToken(refreshToken) { | ||
@@ -55,16 +45,10 @@ const result = await this.client.refreshAccessToken(refreshToken, { | ||
}); | ||
return { | ||
const tokens = { | ||
accessToken: result.access_token, | ||
refreshToken: result.refresh_token ?? null, | ||
accessTokenExpiresAt: createDate(new TimeSpan(result.expires_in, "s")), | ||
idToken: result.id_token, | ||
idTokenClaims: this.parseIdToken(result.id_token) | ||
idToken: result.id_token | ||
}; | ||
return tokens; | ||
} | ||
parseIdToken(idToken) { | ||
const parsedIdToken = parseJWT(idToken); | ||
if (!parsedIdToken) | ||
throw new Error("Failed to parse ID token"); | ||
return parsedIdToken.payload; | ||
} | ||
} |
@@ -11,14 +11,2 @@ import type { OAuth2Provider } from "../index.js"; | ||
accessToken: string; | ||
user: NotionUser; | ||
botId: string; | ||
} | ||
export interface NotionUser { | ||
type: "person"; | ||
id: string; | ||
name: string; | ||
avatar_url: string; | ||
person: NotionPersonUser; | ||
} | ||
export interface NotionPersonUser { | ||
email: string; | ||
} |
@@ -24,8 +24,7 @@ import { OAuth2Client } from "oslo/oauth2"; | ||
}); | ||
return { | ||
accessToken: result.access_token, | ||
user: result.owner, | ||
botId: result.bot_id | ||
const tokens = { | ||
accessToken: result.access_token | ||
}; | ||
return tokens; | ||
} | ||
} |
import type { OAuth2Provider } from "../index.js"; | ||
export declare class Reddit implements OAuth2Provider { | ||
private client; | ||
private scope; | ||
private clientSecret; | ||
private tokenDuration; | ||
constructor(clientId: string, clientSecret: string, redirectURI: string, options?: { | ||
constructor(clientId: string, clientSecret: string, redirectURI: string); | ||
createAuthorizationURL(state: string, options?: { | ||
tokenDuration?: "permanent" | "temporary"; | ||
scope?: string[]; | ||
}); | ||
createAuthorizationURL(state: string): Promise<URL>; | ||
}): Promise<URL>; | ||
validateAuthorizationCode(code: string): Promise<RedditTokens>; | ||
getUser(accessToken: string): Promise<RedditUser>; | ||
refreshAccessToken(refreshToken: string): Promise<RedditTokens>; | ||
@@ -21,151 +18,1 @@ } | ||
} | ||
export interface RedditUser { | ||
is_employee: boolean; | ||
seen_layout_switch: boolean; | ||
has_visited_new_profile: boolean; | ||
pref_no_profanity: boolean; | ||
has_external_account: boolean; | ||
pref_geopopular: string; | ||
seen_redesign_modal: boolean; | ||
pref_show_trending: boolean; | ||
subreddit: { | ||
default_set: boolean; | ||
user_is_contributor: boolean; | ||
banner_img: string; | ||
restrict_posting: boolean; | ||
user_is_banned: boolean; | ||
free_form_reports: boolean; | ||
community_icon: string; | ||
show_media: boolean; | ||
icon_color: string; | ||
user_is_muted: boolean; | ||
display_name: string; | ||
header_img: string; | ||
title: string; | ||
coins: number; | ||
previous_names: string[]; | ||
over_18: boolean; | ||
icon_size: [number, number]; | ||
primary_color: string; | ||
icon_img: string; | ||
description: string; | ||
allowed_media_in_comments: any[]; | ||
submit_link_label: string; | ||
header_size: any; | ||
restrict_commenting: boolean; | ||
subscribers: number; | ||
submit_text_label: string; | ||
is_default_icon: boolean; | ||
link_flair_position: string; | ||
display_name_prefixed: string; | ||
key_color: string; | ||
name: string; | ||
is_default_banner: boolean; | ||
url: string; | ||
quarantine: boolean; | ||
banner_size: [number, number]; | ||
user_is_moderator: boolean; | ||
accept_followers: boolean; | ||
public_description: string; | ||
link_flair_enabled: boolean; | ||
disable_contributor_requests: boolean; | ||
subreddit_type: string; | ||
user_is_subscriber: boolean; | ||
}; | ||
pref_show_presence: boolean; | ||
snoovatar_img: string; | ||
snoovatar_size: [number, number]; | ||
gold_expiration: any; | ||
has_gold_subscription: boolean; | ||
is_sponsor: boolean; | ||
num_friends: number; | ||
features: { | ||
mod_service_mute_writes: boolean; | ||
promoted_trend_blanks: boolean; | ||
show_amp_link: boolean; | ||
chat: boolean; | ||
is_email_permission_required: true; | ||
mod_awards: boolean; | ||
expensive_coins_package: boolean; | ||
mweb_xpromo_revamp_v2: { | ||
owner: string; | ||
variant: string; | ||
experiment_id: number; | ||
}; | ||
awards_on_streams: boolean; | ||
mweb_xpromo_modal_listing_click_daily_dismissible_ios: true; | ||
chat_subreddit: boolean; | ||
cookie_consent_banner: boolean; | ||
modlog_copyright_removal: boolean; | ||
do_not_track: boolean; | ||
images_in_comments: boolean; | ||
mod_service_mute_reads: boolean; | ||
chat_user_settings: boolean; | ||
use_pref_account_deployment: boolean; | ||
mweb_xpromo_interstitial_comments_ios: boolean; | ||
mweb_xpromo_modal_listing_click_daily_dismissible_android: boolean; | ||
premium_subscriptions_table: boolean; | ||
mweb_xpromo_interstitial_comments_android: true; | ||
crowd_control_for_post: boolean; | ||
mweb_nsfw_xpromo: { | ||
owner: string; | ||
variant: string; | ||
experiment_id: number; | ||
}; | ||
noreferrer_to_noopener: boolean; | ||
chat_group_rollout: boolean; | ||
resized_styles_images: boolean; | ||
spez_modal: boolean; | ||
mweb_sharing_clipboard: { | ||
owner: string; | ||
variant: string; | ||
experiment_id: number; | ||
}; | ||
}; | ||
can_edit_name: boolean; | ||
verified: boolean; | ||
pref_autoplay: boolean; | ||
coins: number; | ||
has_paypal_subscription: boolean; | ||
has_subscribed_to_premium: boolean; | ||
id: string; | ||
has_stripe_subscription: boolean; | ||
oauth_client_id: string; | ||
can_create_subreddit: boolean; | ||
over_18: boolean; | ||
is_gold: boolean; | ||
is_mod: boolean; | ||
awarder_karma: number; | ||
suspension_expiration_utc: any; | ||
has_verified_email: boolean; | ||
is_suspended: boolean; | ||
pref_video_autoplay: boolean; | ||
has_android_subscription: boolean; | ||
in_redesign_beta: boolean; | ||
icon_img: string; | ||
pref_nightmode: boolean; | ||
awardee_karma: number; | ||
hide_from_robots: boolean; | ||
password_set: boolean; | ||
link_karma: number; | ||
force_password_reset: boolean; | ||
total_karma: number; | ||
seen_give_award_tooltip: boolean; | ||
inbox_count: number; | ||
seen_premium_adblock_modal: boolean; | ||
pref_top_karma_subreddits: boolean; | ||
pref_show_snoovatar: boolean; | ||
name: string; | ||
pref_clickgadget: number; | ||
created: number; | ||
gold_creddits: number; | ||
created_utc: number; | ||
has_ios_subscription: boolean; | ||
pref_show_twitter: boolean; | ||
in_beta: boolean; | ||
comment_karma: number; | ||
accept_followers: boolean; | ||
has_subscribed: boolean; | ||
linked_identities: any[]; | ||
seen_subreddit_chat_ftux: boolean; | ||
} |
@@ -7,19 +7,15 @@ import { OAuth2Client } from "oslo/oauth2"; | ||
client; | ||
scope; | ||
clientSecret; | ||
tokenDuration; | ||
constructor(clientId, clientSecret, redirectURI, options) { | ||
constructor(clientId, clientSecret, redirectURI) { | ||
this.client = new OAuth2Client(clientId, authorizeEndpoint, tokenEndpoint, { | ||
redirectURI | ||
}); | ||
this.scope = options?.scope ?? []; | ||
this.clientSecret = clientSecret; | ||
this.tokenDuration = options?.tokenDuration ?? "temporary"; | ||
} | ||
async createAuthorizationURL(state) { | ||
async createAuthorizationURL(state, options) { | ||
const url = await this.client.createAuthorizationURL({ | ||
state, | ||
scope: this.scope | ||
scope: options?.scope ?? [] | ||
}); | ||
url.searchParams.set("duration", this.tokenDuration); | ||
url.searchParams.set("duration", options?.tokenDuration ?? "temporary"); | ||
return url; | ||
@@ -31,3 +27,3 @@ } | ||
}); | ||
return { | ||
const tokens = { | ||
accessToken: result.access_token, | ||
@@ -37,11 +33,4 @@ accessTokenExpiresAt: createDate(new TimeSpan(result.expires_in, "s")), | ||
}; | ||
return tokens; | ||
} | ||
async getUser(accessToken) { | ||
const response = await fetch("https://oauth.reddit.com/api/v1/me", { | ||
headers: { | ||
Authorization: `Bearer ${accessToken}` | ||
} | ||
}); | ||
return await response.json(); | ||
} | ||
async refreshAccessToken(refreshToken) { | ||
@@ -51,3 +40,3 @@ const result = await this.client.refreshAccessToken(refreshToken, { | ||
}); | ||
return { | ||
const tokens = { | ||
accessToken: result.access_token, | ||
@@ -57,3 +46,4 @@ accessTokenExpiresAt: createDate(new TimeSpan(result.expires_in, "s")), | ||
}; | ||
return tokens; | ||
} | ||
} |
@@ -1,12 +0,10 @@ | ||
import type { OAuth2ProviderWithPKCE } from "../index.js"; | ||
export declare class Slack implements OAuth2ProviderWithPKCE { | ||
import type { OAuth2Provider } from "../index.js"; | ||
export declare class Slack implements OAuth2Provider { | ||
private client; | ||
private scope; | ||
private clientSecret; | ||
constructor(clientId: string, clientSecret: string, redirectURI: string, options?: { | ||
constructor(clientId: string, clientSecret: string, redirectURI: string); | ||
createAuthorizationURL(state: string, options?: { | ||
scope?: string[]; | ||
}); | ||
createAuthorizationURL(codeVerifier: string): Promise<URL>; | ||
validateAuthorizationCode(code: string, codeVerifier: string): Promise<SlackTokens>; | ||
getUser(accessToken: string): Promise<SlackUser>; | ||
}): Promise<URL>; | ||
validateAuthorizationCode(code: string): Promise<SlackTokens>; | ||
} | ||
@@ -17,30 +15,1 @@ export interface SlackTokens { | ||
} | ||
export interface SlackUser { | ||
sub: string; | ||
"https://slack.com/user_id": string; | ||
"https://slack.com/team_id": string; | ||
email?: string; | ||
email_verified: boolean; | ||
date_email_verified: number; | ||
name: string; | ||
picture: string; | ||
given_name: string; | ||
family_name: string; | ||
locale: string; | ||
"https://slack.com/team_name": string; | ||
"https://slack.com/team_domain": string; | ||
"https://slack.com/user_image_24": string; | ||
"https://slack.com/user_image_32": string; | ||
"https://slack.com/user_image_48": string; | ||
"https://slack.com/user_image_72": string; | ||
"https://slack.com/user_image_192": string; | ||
"https://slack.com/user_image_512": string; | ||
"https://slack.com/team_image_34": string; | ||
"https://slack.com/team_image_44": string; | ||
"https://slack.com/team_image_68": string; | ||
"https://slack.com/team_image_88": string; | ||
"https://slack.com/team_image_102": string; | ||
"https://slack.com/team_image_132": string; | ||
"https://slack.com/team_image_230": string; | ||
"https://slack.com/team_image_default": true; | ||
} |
@@ -6,37 +6,28 @@ import { OAuth2Client } from "oslo/oauth2"; | ||
client; | ||
scope; | ||
clientSecret; | ||
constructor(clientId, clientSecret, redirectURI, options) { | ||
constructor(clientId, clientSecret, redirectURI) { | ||
this.client = new OAuth2Client(clientId, authorizeEndpoint, tokenEndpoint, { | ||
redirectURI | ||
}); | ||
this.scope = options?.scope ?? []; | ||
this.scope.push("openid", "profile"); | ||
this.clientSecret = clientSecret; | ||
} | ||
async createAuthorizationURL(codeVerifier) { | ||
async createAuthorizationURL(state, options) { | ||
const scope = options?.scope ?? []; | ||
scope.push("openid"); | ||
return await this.client.createAuthorizationURL({ | ||
codeVerifier, | ||
scope: this.scope | ||
state, | ||
scope | ||
}); | ||
} | ||
async validateAuthorizationCode(code, codeVerifier) { | ||
async validateAuthorizationCode(code) { | ||
const result = await this.client.validateAuthorizationCode(code, { | ||
authenticateWith: "request_body", | ||
credentials: this.clientSecret, | ||
codeVerifier | ||
credentials: this.clientSecret | ||
}); | ||
return { | ||
const tokens = { | ||
accessToken: result.access_token, | ||
idToken: result.id_token | ||
}; | ||
return tokens; | ||
} | ||
async getUser(accessToken) { | ||
const response = await fetch("https://slack.com/api/openid.connect.userInfo", { | ||
headers: { | ||
Authorization: `Bearer ${accessToken}` | ||
} | ||
}); | ||
return await response.json(); | ||
} | ||
} |
@@ -5,9 +5,7 @@ import type { OAuth2Provider } from "../index.js"; | ||
private clientSecret; | ||
private scope; | ||
constructor(clientId: string, clientSecret: string, redirectURI: string, options?: { | ||
constructor(clientId: string, clientSecret: string, redirectURI: string); | ||
createAuthorizationURL(state: string, options?: { | ||
scope?: string[]; | ||
}); | ||
createAuthorizationURL(state: string): Promise<URL>; | ||
}): Promise<URL>; | ||
validateAuthorizationCode(code: string): Promise<SpotifyTokens>; | ||
getUser(accessToken: string): Promise<SpotifyUser>; | ||
refreshAccessToken(refreshToken: string): Promise<SpotifyTokens>; | ||
@@ -20,29 +18,1 @@ } | ||
} | ||
export interface SpotifyUser { | ||
country?: string; | ||
display_name: string | null; | ||
email?: string; | ||
explicit_content: { | ||
filter_enabled?: boolean; | ||
filter_locked?: boolean; | ||
}; | ||
external_urls: { | ||
spotify: string; | ||
}; | ||
followers: { | ||
href: string | null; | ||
total: number; | ||
}; | ||
href: string; | ||
id: string; | ||
images: [ | ||
{ | ||
url: string; | ||
height: number | null; | ||
width: number | null; | ||
} | ||
]; | ||
product?: string; | ||
type: string; | ||
uri: string; | ||
} |
@@ -8,4 +8,3 @@ import { TimeSpan, createDate } from "oslo"; | ||
clientSecret; | ||
scope; | ||
constructor(clientId, clientSecret, redirectURI, options) { | ||
constructor(clientId, clientSecret, redirectURI) { | ||
this.client = new OAuth2Client(clientId, authorizeEndpoint, tokenEndpoint, { | ||
@@ -15,8 +14,7 @@ redirectURI | ||
this.clientSecret = clientSecret; | ||
this.scope = options?.scope ?? []; | ||
} | ||
async createAuthorizationURL(state) { | ||
async createAuthorizationURL(state, options) { | ||
return await this.client.createAuthorizationURL({ | ||
state, | ||
scope: this.scope | ||
scope: options?.scope ?? [] | ||
}); | ||
@@ -28,3 +26,3 @@ } | ||
}); | ||
return { | ||
const tokens = { | ||
accessToken: result.access_token, | ||
@@ -34,14 +32,7 @@ refreshToken: result.refresh_token, | ||
}; | ||
return tokens; | ||
} | ||
async getUser(accessToken) { | ||
const response = await fetch("https://api.spotify.com/v1/me", { | ||
headers: { | ||
Authorization: `Bearer ${accessToken}` | ||
} | ||
}); | ||
return await response.json(); | ||
} | ||
async refreshAccessToken(refreshToken) { | ||
const result = await this.client.refreshAccessToken(refreshToken); | ||
return { | ||
const tokens = { | ||
accessToken: result.access_token, | ||
@@ -51,3 +42,4 @@ refreshToken: result.refresh_token, | ||
}; | ||
return tokens; | ||
} | ||
} |
import type { OAuth2Provider } from "../index.js"; | ||
export declare class Twitch implements OAuth2Provider { | ||
private client; | ||
private scope; | ||
private clientSecret; | ||
constructor(clientId: string, clientSecret: string, redirectURI: string, options?: { | ||
constructor(clientId: string, clientSecret: string, redirectURI: string); | ||
createAuthorizationURL(state: string, options?: { | ||
scope?: string[]; | ||
}); | ||
createAuthorizationURL(state: string): Promise<URL>; | ||
}): Promise<URL>; | ||
validateAuthorizationCode(code: string): Promise<TwitchTokens>; | ||
getUser(accessToken: string): Promise<TwitchUser>; | ||
refreshAccessToken(refreshToken: string): Promise<TwitchTokens>; | ||
@@ -19,14 +17,1 @@ } | ||
} | ||
export interface TwitchUser { | ||
id: string; | ||
login: string; | ||
display_name: string; | ||
type: "" | "admin" | "staff" | "global_mod"; | ||
broadcaster_type: "" | "affiliate" | "partner"; | ||
description: string; | ||
profile_image_url: string; | ||
offline_image_url: string; | ||
view_count: number; | ||
email?: string; | ||
created_at: string; | ||
} |
@@ -7,15 +7,13 @@ import { TimeSpan, createDate } from "oslo"; | ||
client; | ||
scope; | ||
clientSecret; | ||
constructor(clientId, clientSecret, redirectURI, options) { | ||
constructor(clientId, clientSecret, redirectURI) { | ||
this.client = new OAuth2Client(clientId, authorizeEndpoint, tokenEndpoint, { | ||
redirectURI | ||
}); | ||
this.scope = options?.scope ?? []; | ||
this.clientSecret = clientSecret; | ||
} | ||
async createAuthorizationURL(state) { | ||
async createAuthorizationURL(state, options) { | ||
return await this.client.createAuthorizationURL({ | ||
state, | ||
scope: this.scope | ||
scope: options?.scope ?? [] | ||
}); | ||
@@ -28,3 +26,3 @@ } | ||
}); | ||
return { | ||
const tokens = { | ||
accessToken: result.access_token, | ||
@@ -34,11 +32,4 @@ refreshToken: result.refresh_token, | ||
}; | ||
return tokens; | ||
} | ||
async getUser(accessToken) { | ||
const response = await fetch("https://api.twitch.tv/helix/users", { | ||
headers: { | ||
Authorization: `Bearer ${accessToken}` | ||
} | ||
}); | ||
return await response.json(); | ||
} | ||
async refreshAccessToken(refreshToken) { | ||
@@ -49,3 +40,3 @@ const result = await this.client.refreshAccessToken(refreshToken, { | ||
}); | ||
return { | ||
const tokens = { | ||
accessToken: result.access_token, | ||
@@ -55,3 +46,4 @@ refreshToken: result.refresh_token, | ||
}; | ||
return tokens; | ||
} | ||
} |
import type { OAuth2ProviderWithPKCE } from "../index.js"; | ||
export declare class Twitter implements OAuth2ProviderWithPKCE { | ||
private client; | ||
private scope; | ||
private clientSecret; | ||
constructor(clientId: string, clientSecret: string, redirectURI: string, options?: { | ||
constructor(clientId: string, clientSecret: string, redirectURI: string); | ||
createAuthorizationURL(codeVerifier: string, options?: { | ||
scope?: string[]; | ||
}); | ||
createAuthorizationURL(codeVerifier: string): Promise<URL>; | ||
}): Promise<URL>; | ||
validateAuthorizationCode(code: string, codeVerifier: string): Promise<TwitterTokens>; | ||
getUser(accessToken: string): Promise<TwitterUser>; | ||
refreshAccessToken(refreshToken: string): Promise<TwitterTokens>; | ||
@@ -18,6 +16,1 @@ } | ||
} | ||
export interface TwitterUser { | ||
id: string; | ||
name: string; | ||
username: string; | ||
} |
@@ -6,17 +6,14 @@ import { OAuth2Client, generateState } from "oslo/oauth2"; | ||
client; | ||
scope; | ||
clientSecret; | ||
constructor(clientId, clientSecret, redirectURI, options) { | ||
constructor(clientId, clientSecret, redirectURI) { | ||
this.client = new OAuth2Client(clientId, authorizeEndpoint, tokenEndpoint, { | ||
redirectURI | ||
}); | ||
this.scope = options?.scope ?? []; | ||
this.scope.push("users.read"); | ||
this.clientSecret = clientSecret; | ||
} | ||
async createAuthorizationURL(codeVerifier) { | ||
async createAuthorizationURL(codeVerifier, options) { | ||
return await this.client.createAuthorizationURL({ | ||
state: generateState(), | ||
scope: this.scope, | ||
codeVerifier | ||
codeVerifier, | ||
scope: options?.scope ?? [] | ||
}); | ||
@@ -29,15 +26,8 @@ } | ||
}); | ||
return { | ||
const tokens = { | ||
accessToken: result.access_token, | ||
refreshToken: result.refresh_token ?? null | ||
}; | ||
return tokens; | ||
} | ||
async getUser(accessToken) { | ||
const response = await fetch("https://api.twitter.com/2/users/me", { | ||
headers: { | ||
Authorization: `Bearer ${accessToken}` | ||
} | ||
}); | ||
return await response.json(); | ||
} | ||
async refreshAccessToken(refreshToken) { | ||
@@ -48,7 +38,8 @@ const result = await this.client.refreshAccessToken(refreshToken, { | ||
}); | ||
return { | ||
const tokens = { | ||
accessToken: result.access_token, | ||
refreshToken: result.refresh_token ?? null | ||
}; | ||
return tokens; | ||
} | ||
} |
{ | ||
"name": "arctic", | ||
"type": "module", | ||
"version": "0.7.1", | ||
"version": "0.8.0", | ||
"description": "OAuth 2.0 with built-in providers", | ||
@@ -6,0 +6,0 @@ "main": "dist/index.js", |
226
README.md
@@ -1,19 +0,31 @@ | ||
# `arctic` | ||
# Arctic | ||
Library for handling OAuth 2.0 with built-in providers. Light weight, fully-typed, runtime-agnostic. Built using [`oslo`](http://github.com/pilcrowonpaper/oslo). For a more flexible OAuth 2.0 client, see [`oslo/oauth2`](http://github.com/pilcrowonpaper/oslo). | ||
Arctic is an OAuth 2.0 library for JavaScript/TypeScript that supports numerous providers. It's light weight, fully-typed, and runtime-agnostic. | ||
```ts | ||
import { GitHub, generateState } from "arctic"; | ||
const github = new GitHub(clientId, clientSecret); | ||
const state = generateState(); | ||
const authorizationURL = await github.createAuthorizationURL(state, { | ||
scope: ["user:email"] | ||
}); | ||
const tokens = await github.validateAuthorizationCode(code); | ||
``` | ||
npm install arctic | ||
``` | ||
## Providers | ||
[Read the documentation](./docs/main.md) | ||
### OAuth 2.0 | ||
> For a more flexible OAuth 2.0 client, see [`oslo/oauth2`](http://github.com/pilcrowonpaper/oslo). | ||
See [OAuth 2.0 providers](#oauth-20-providers) for instructions. | ||
## Supported providers | ||
- Apple | ||
- Atlassian | ||
- Auth0 | ||
- Bitbucket | ||
- Box | ||
- Discord | ||
- Dropbox | ||
@@ -23,4 +35,8 @@ - Figma | ||
- GitLab | ||
- Kakao | ||
- Keycloak | ||
- Line | ||
- Microsoft Entra ID | ||
- Notion | ||
@@ -30,197 +46,3 @@ - Reddit | ||
- Twitch | ||
### OAuth 2.0 with PKCE | ||
See [OAuth 2.0 providers with PKCE](#oauth-20-providers-with-pkce) for instructions. | ||
- Keycloak | ||
- Line | ||
- Microsoft Entra ID | ||
## OAuth 2.0 providers | ||
Most providers require the `client_id` and `client_secret`. You may also optionally pass `scope`. For OIDC clients, `openid` and `profile` scope are always included. | ||
```ts | ||
import { GitHub } from "arctic"; | ||
const github = new GitHub(clientId, clientSecret, { | ||
scope: ["user:email"] // etc | ||
}); | ||
``` | ||
Some providers also require the redirect URI. | ||
```ts | ||
import { Google } from "arctic"; | ||
const redirectURI = "http://localhost:3000/login/google/callback"; | ||
const google = new Google(clientId, clientSecret, redirectURI); | ||
``` | ||
### Create authorization URL | ||
Generate state using `generateState()` and store it as a cookie. Use it to create an authorization URL with `createAuthorizationURL()` and redirect the user to it. | ||
```ts | ||
import { generateState } from "arctic"; | ||
const state = generateState(); | ||
const url = await github.createAuthorizationURL(state); | ||
// store state as cookie | ||
setCookie("state", state, { | ||
secure: true, // set to false in localhost | ||
path: "/", | ||
httpOnly: true, | ||
maxAge: 60 * 10 // 10 min | ||
}); | ||
return redirect(url); | ||
``` | ||
### Validate authorization code | ||
Compare the state, and use `validateAuthorizationCode()` to validate the authorization code. This returns an object with an access token, and a refresh token if requested. If the code is invalid, it will throw an `AccessTokenRequestError`. | ||
```ts | ||
import { OAuth2RequestError } from "arctic"; | ||
const code = request.url.searchParams.get("code"); | ||
const state = request.url.searchParams.get("state"); | ||
const storedState = getCookie("state"); | ||
if (!code || !storedState || state !== storedState) { | ||
// 400 | ||
throw new Error("Invalid request"); | ||
} | ||
try { | ||
const tokens = await github.validateAuthorizationCode(code); | ||
} catch (e) { | ||
if (e instanceof OAuth2RequestError) { | ||
const { message, description, request } = e; | ||
} | ||
// unknown error | ||
} | ||
``` | ||
### Other | ||
See also: | ||
- [Get the current user](#get-the-current-user) | ||
- [Refresh access tokens](#refresh-access-tokens) | ||
## OAuth 2.0 providers with PKCE | ||
Most providers require the `client_id` and `client_secret`. You may also optionally pass `scope`. For OIDC clients, `openid` and `profile` scope are always included. | ||
```ts | ||
import { GitHub } from "arctic"; | ||
const github = new GitHub(clientId, clientSecret, { | ||
scope: ["user:email"] // etc | ||
}); | ||
``` | ||
Some providers also require the redirect URI. | ||
```ts | ||
import { Google } from "arctic"; | ||
const redirectURI = "http://localhost:3000/login/google/callback"; | ||
const google = new Google(clientId, clientSecret, redirectURI); | ||
``` | ||
### Create authorization URL | ||
When using the PKCE flow, `state` is not necessary. Generate a code verifier using `generateCodeVerifier()`, and store it as a cookie. Use them to create an authorization URL with `createAuthorizationURL()` and redirect the user to it. | ||
```ts | ||
import { generateState, generateCodeVerifier } from "arctic"; | ||
const codeVerifier = generateCodeVerifier(); | ||
const url = await github.createAuthorizationURL(state, codeVerifier); | ||
// store code verifier as cookie | ||
setCookie("code_verifier", state, { | ||
secure: true, // set to false in localhost | ||
path: "/", | ||
httpOnly: true, | ||
maxAge: 60 * 10 // 10 min | ||
}); | ||
return redirect(url); | ||
``` | ||
### Validate authorization code | ||
Use `validateAuthorizationCode()` to validate the authorization code with the code verifier. This returns an object with an access token, and a refresh token if requested. If the code is invalid, it will throw an `AccessTokenRequestError`. | ||
```ts | ||
import { OAuth2RequestError } from "arctic"; | ||
const code = request.url.searchParams.get("code"); | ||
const codeVerifier = request.url.searchParams.get("code_verifier"); | ||
try { | ||
const tokens = await github.validateAuthorizationCode(code, codeVerifier); | ||
} catch (e) { | ||
if (e instanceof OAuth2RequestError) { | ||
// see https://oslo.js.org/reference/oauth2/OAuth2RequestError/ | ||
const { request, message, description } = e; | ||
} | ||
// unknown error | ||
} | ||
``` | ||
### Other | ||
See also: | ||
- [Get the current user](#get-the-current-user) | ||
- [Refresh access tokens](#refresh-access-tokens) | ||
## Other | ||
### Get the current user | ||
Some providers expose `getUser()` to get the current user from an access token. This will throw an `RequestError` if the access token is invalid. | ||
```ts | ||
import { RequestError } from "arctic"; | ||
try { | ||
const user = await github.getUser(tokens.accessToken); | ||
} catch (e) { | ||
if (e instanceof RequestError) { | ||
// get fetch Request and Response | ||
const { request, response } = e; | ||
} | ||
// unknown error | ||
} | ||
``` | ||
### Refresh access tokens | ||
Some providers expose `refreshAccessToken()` to get a new access token from a refresh token. This will throw an `OAuth2RequestError` if the refresh token is invalid. | ||
```ts | ||
import { OAuth2RequestError } from "arctic"; | ||
try { | ||
const tokens = await google.refreshAccessToken(refreshToken); | ||
} catch (e) { | ||
if (e instanceof OAuth2RequestError) { | ||
// see https://oslo.js.org/reference/oauth2/OAuth2RequestError/ | ||
const { request, message, description } = e; | ||
} | ||
// unknown error | ||
} | ||
``` | ||
- Yahoo |
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
53
0
60994
1554
47