arctic
Advanced tools
Comparing version 0.3.6 to 0.4.0
export { Apple } from "./providers/apple.js"; | ||
export { AzureAD } from "./providers/azure-ad.js"; | ||
export { MicrosoftEntra } from "./providers/microsoft-entra.js"; | ||
export { Discord } from "./providers/discord.js"; | ||
@@ -12,3 +12,3 @@ export { Facebook } from "./providers/facebook.js"; | ||
export type { AppleCredentials, AppleIdTokenClaims, AppleRefreshedTokens, AppleTokens } from "./providers/apple.js"; | ||
export type { AzureADIdTokenClaims, AzureADTokens, AzureADUser } from "./providers/azure-ad.js"; | ||
export type { MicrosoftEntraIdTokenClaims, MicrosoftEntraTokens, MicrosoftEntraUser } from "./providers/microsoft-entra.js"; | ||
export type { DiscordTokens, DiscordUser } from "./providers/discord.js"; | ||
@@ -23,1 +23,16 @@ export type { FacebookTokens, FacebookUser } from "./providers/facebook.js"; | ||
export { generateCodeVerifier, generateState, OAuth2RequestError } from "oslo/oauth2"; | ||
export interface OAuth2Provider { | ||
createAuthorizationURL(state: string): Promise<URL>; | ||
validateAuthorizationCode(code: string): Promise<Tokens>; | ||
refreshAccessToken?(refreshToken: string): Promise<Tokens>; | ||
getUser?(accessToken: string): Promise<{}>; | ||
} | ||
export interface OAuth2ProviderWithPKCE { | ||
createAuthorizationURL(codeVerifier: string): Promise<URL>; | ||
validateAuthorizationCode(code: string, codeVerifier: string): Promise<Tokens>; | ||
refreshAccessToken?(refreshToken: string): Promise<Tokens>; | ||
getUser?(accessToken: string): Promise<{}>; | ||
} | ||
export interface Tokens { | ||
accessToken: string; | ||
} |
export { Apple } from "./providers/apple.js"; | ||
export { AzureAD } from "./providers/azure-ad.js"; | ||
export { MicrosoftEntra } from "./providers/microsoft-entra.js"; | ||
export { Discord } from "./providers/discord.js"; | ||
@@ -4,0 +4,0 @@ export { Facebook } from "./providers/facebook.js"; |
@@ -1,2 +0,3 @@ | ||
export declare class Apple { | ||
import type { OAuth2Provider } from "../index.js"; | ||
export declare class Apple implements OAuth2Provider { | ||
private client; | ||
@@ -3,0 +4,0 @@ private scope; |
@@ -1,2 +0,3 @@ | ||
export declare class Discord { | ||
import type { OAuth2Provider } from "../index.js"; | ||
export declare class Discord implements OAuth2Provider { | ||
private client; | ||
@@ -3,0 +4,0 @@ private scope; |
@@ -1,2 +0,3 @@ | ||
export declare class Facebook { | ||
import type { OAuth2Provider } from "../index.js"; | ||
export declare class Facebook implements OAuth2Provider { | ||
private client; | ||
@@ -3,0 +4,0 @@ private scope; |
@@ -1,2 +0,3 @@ | ||
export declare class GitHub { | ||
import type { OAuth2Provider } from "../index.js"; | ||
export declare class GitHub implements OAuth2Provider { | ||
private client; | ||
@@ -3,0 +4,0 @@ private scope; |
@@ -1,2 +0,3 @@ | ||
export declare class Google { | ||
import type { OAuth2Provider } from "../index.js"; | ||
export declare class Google implements OAuth2Provider { | ||
private client; | ||
@@ -10,4 +11,4 @@ private scope; | ||
}); | ||
createAuthorizationURL(state: string, codeVerifier?: string): Promise<URL>; | ||
validateAuthorizationCode(code: string, codeVerifier?: string): Promise<GoogleTokens>; | ||
createAuthorizationURL(state: string): Promise<URL>; | ||
validateAuthorizationCode(code: string): Promise<GoogleTokens>; | ||
getUser(accessToken: string): Promise<GoogleUser>; | ||
@@ -14,0 +15,0 @@ refreshAccessToken(refreshToken: string): Promise<GoogleRefreshedTokens>; |
@@ -20,7 +20,6 @@ import { TimeSpan, createDate } from "oslo"; | ||
} | ||
async createAuthorizationURL(state, codeVerifier) { | ||
async createAuthorizationURL(state) { | ||
const url = await this.client.createAuthorizationURL({ | ||
state, | ||
scope: this.scope, | ||
codeVerifier | ||
state | ||
}); | ||
@@ -30,7 +29,6 @@ url.searchParams.set("access_type", this.accessType); | ||
} | ||
async validateAuthorizationCode(code, codeVerifier) { | ||
async validateAuthorizationCode(code) { | ||
const result = await this.client.validateAuthorizationCode(code, { | ||
authenticateWith: "request_body", | ||
credentials: this.clientSecret, | ||
codeVerifier | ||
credentials: this.clientSecret | ||
}); | ||
@@ -37,0 +35,0 @@ return { |
@@ -1,2 +0,3 @@ | ||
export declare class Line { | ||
import type { OAuth2ProviderWithPKCE } from "../index.js"; | ||
export declare class Line implements OAuth2ProviderWithPKCE { | ||
private client; | ||
@@ -8,4 +9,4 @@ private scope; | ||
}); | ||
createAuthorizationURL(state: string): Promise<URL>; | ||
validateAuthorizationCode(code: string): Promise<LineTokens>; | ||
createAuthorizationURL(codeVerifier: string): Promise<URL>; | ||
validateAuthorizationCode(code: string, codeVerifier: string): Promise<LineTokens>; | ||
getUser(accessToken: string): Promise<LineUser>; | ||
@@ -12,0 +13,0 @@ refreshAccessToken(refreshToken: string): Promise<LineRefreshedTokens>; |
import { TimeSpan, createDate } from "oslo"; | ||
import { parseJWT } from "oslo/jwt"; | ||
import { OAuth2Client } from "oslo/oauth2"; | ||
import { OAuth2Client, generateState } from "oslo/oauth2"; | ||
const authorizeEndpoint = "https://access.line.me/oauth2/v2.1/authorize"; | ||
@@ -18,12 +18,14 @@ const tokenEndpoint = "https://api.line.me/oauth2/v2.1/token"; | ||
} | ||
async createAuthorizationURL(state) { | ||
async createAuthorizationURL(codeVerifier) { | ||
return await this.client.createAuthorizationURL({ | ||
state, | ||
state: generateState(), | ||
codeVerifier, | ||
scope: this.scope | ||
}); | ||
} | ||
async validateAuthorizationCode(code) { | ||
async validateAuthorizationCode(code, codeVerifier) { | ||
const result = await this.client.validateAuthorizationCode(code, { | ||
authenticateWith: "request_body", | ||
credentials: this.clientSecret | ||
credentials: this.clientSecret, | ||
codeVerifier | ||
}); | ||
@@ -30,0 +32,0 @@ const parsedIdToken = parseJWT(result.id_token); |
@@ -1,10 +0,10 @@ | ||
export declare class Spotify { | ||
import type { OAuth2ProviderWithPKCE } from "../index.js"; | ||
export declare class Spotify implements OAuth2ProviderWithPKCE { | ||
private client; | ||
private scope; | ||
private clientSecret; | ||
constructor(clientId: string, clientSecret: string, redirectURI: string, options?: { | ||
constructor(clientId: string, redirectURI: string, options?: { | ||
scope?: string[]; | ||
}); | ||
createAuthorizationURL(state: string): Promise<URL>; | ||
validateAuthorizationCode(code: string): Promise<SpotifyTokens>; | ||
createAuthorizationURL(codeVerifier: string): Promise<URL>; | ||
validateAuthorizationCode(code: string, codeVerifier: string): Promise<SpotifyTokens>; | ||
getUser(accessToken: string): Promise<SpotifyUser>; | ||
@@ -11,0 +11,0 @@ refreshAccessToken(refreshToken: string): Promise<SpotifyTokens>; |
@@ -8,4 +8,3 @@ import { TimeSpan, createDate } from "oslo"; | ||
scope; | ||
clientSecret; | ||
constructor(clientId, clientSecret, redirectURI, options) { | ||
constructor(clientId, redirectURI, options) { | ||
this.client = new OAuth2Client(clientId, authorizeEndpoint, tokenEndpoint, { | ||
@@ -15,13 +14,12 @@ redirectURI | ||
this.scope = options?.scope ?? []; | ||
this.clientSecret = clientSecret; | ||
} | ||
async createAuthorizationURL(state) { | ||
async createAuthorizationURL(codeVerifier) { | ||
return await this.client.createAuthorizationURL({ | ||
state, | ||
codeVerifier, | ||
scope: this.scope | ||
}); | ||
} | ||
async validateAuthorizationCode(code) { | ||
async validateAuthorizationCode(code, codeVerifier) { | ||
const result = await this.client.validateAuthorizationCode(code, { | ||
credentials: this.clientSecret | ||
codeVerifier | ||
}); | ||
@@ -28,0 +26,0 @@ return { |
@@ -1,2 +0,3 @@ | ||
export declare class Twitch { | ||
import type { OAuth2Provider } from "../index.js"; | ||
export declare class Twitch implements OAuth2Provider { | ||
private client; | ||
@@ -3,0 +4,0 @@ private scope; |
@@ -1,2 +0,3 @@ | ||
export declare class Twitter { | ||
import type { OAuth2ProviderWithPKCE } from "../index.js"; | ||
export declare class Twitter implements OAuth2ProviderWithPKCE { | ||
private client; | ||
@@ -8,3 +9,3 @@ private scope; | ||
}); | ||
createAuthorizationURL(state: string, codeVerifier: string): Promise<URL>; | ||
createAuthorizationURL(codeVerifier: string): Promise<URL>; | ||
validateAuthorizationCode(code: string, codeVerifier: string): Promise<TwitterTokens>; | ||
@@ -11,0 +12,0 @@ getUser(accessToken: string): Promise<TwitterUser>; |
@@ -1,2 +0,2 @@ | ||
import { OAuth2Client } from "oslo/oauth2"; | ||
import { OAuth2Client, generateState } from "oslo/oauth2"; | ||
const authorizeEndpoint = "https://twitter.com/i/oauth2/authorize"; | ||
@@ -16,5 +16,5 @@ const tokenEndpoint = "https://api.twitter.com/2/oauth2/token"; | ||
} | ||
async createAuthorizationURL(state, codeVerifier) { | ||
async createAuthorizationURL(codeVerifier) { | ||
return await this.client.createAuthorizationURL({ | ||
state, | ||
state: generateState(), | ||
scope: this.scope, | ||
@@ -21,0 +21,0 @@ codeVerifier |
{ | ||
"name": "arctic", | ||
"type": "module", | ||
"version": "0.3.6", | ||
"version": "0.4.0", | ||
"description": "OAuth 2.0 with built-in providers", | ||
@@ -6,0 +6,0 @@ "main": "dist/index.js", |
@@ -11,15 +11,24 @@ # `arctic` | ||
- [Apple](#oauth-20) | ||
- [Azure AD](#oauth-20-with-pkce-flow) | ||
- [Discord](#oauth-20) | ||
- [Facebook](#oauth-20) | ||
- [GitHub](#oauth-20) | ||
- [Google](#oauth-20) | ||
- [LINE](#oauth-20) | ||
- [Spotify](#oauth-20) | ||
- [Twitch](#oauth-20) | ||
- [Twitter](#oauth-20-with-pkce-flow) | ||
### OAuth 2.0 | ||
## OAuth 2.0 | ||
See [OAuth 2.0 providers](#oauth-20-providers) for instructions. | ||
- Apple | ||
- Discord | ||
- Github | ||
- Twitch | ||
### OAuth 2.0 with PKCE | ||
See [OAuth 2.0 providers with PKCE](#oauth-20-providers-with-pkce) for instructions. | ||
- Line | ||
- Microsoft Entra | ||
- Spotify | ||
## 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. | ||
@@ -100,3 +109,3 @@ | ||
## OAuth 2.0 with PKCE flow | ||
## OAuth 2.0 providers with PKCE | ||
@@ -125,3 +134,3 @@ 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. | ||
Generate state and code verifier using `generateState()` and `generateCodeVerifier()`, and store them as cookies. Use them to create an authorization URL with `createAuthorizationURL()` and redirect the user to it. | ||
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. | ||
@@ -131,3 +140,2 @@ ```ts | ||
const state = generateState(); | ||
const codeVerifier = generateCodeVerifier(); | ||
@@ -137,9 +145,3 @@ | ||
// store state and code verifier as cookie | ||
setCookie("state", state, { | ||
secure: true, // set to false in localhost | ||
path: "/", | ||
httpOnly: true, | ||
maxAge: 60 * 10 // 10 min | ||
}); | ||
// store code verifier as cookie | ||
setCookie("code_verifier", state, { | ||
@@ -156,3 +158,3 @@ secure: true, // set to false in localhost | ||
Compare the state, and 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`. | ||
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`. | ||
@@ -163,12 +165,4 @@ ```ts | ||
const code = request.url.searchParams.get("code"); | ||
const state = request.url.searchParams.get("state"); | ||
const codeVerifier = request.url.searchParams.get("code_verifier"); | ||
const storedState = getCookie("state"); | ||
if (!code || !codeVerifier || state !== storedState) { | ||
// 400 | ||
throw new Error("Invalid request"); | ||
} | ||
try { | ||
@@ -175,0 +169,0 @@ const tokens = await github.validateAuthorizationCode(code, codeVerifier); |
42712
1018
216