@oslojs/oauth2
Advanced tools
Comparing version 0.1.2 to 0.2.0
@@ -11,3 +11,3 @@ import { OAuth2RequestContext } from "./request.js"; | ||
} | ||
export declare class AuthorizationCodeAccessTokenRequestContext extends OAuth2RequestContext { | ||
export declare class AuthorizationCodeTokenRequestContext extends OAuth2RequestContext { | ||
constructor(authorizationCode: string); | ||
@@ -14,0 +14,0 @@ setCodeVerifier(codeVerifier: string): void; |
@@ -41,5 +41,5 @@ import { base64url } from "@oslojs/encoding"; | ||
} | ||
export class AuthorizationCodeAccessTokenRequestContext extends OAuth2RequestContext { | ||
export class AuthorizationCodeTokenRequestContext extends OAuth2RequestContext { | ||
constructor(authorizationCode) { | ||
super(); | ||
super("POST"); | ||
this.body.set("grant_type", "authorization_code"); | ||
@@ -46,0 +46,0 @@ this.body.set("code", authorizationCode); |
@@ -1,2 +0,2 @@ | ||
import { OAuth2RequestContext } from "./request.js"; | ||
import { OAuth2RequestContext, OAuth2RequestResult } from "./request.js"; | ||
export declare class DeviceAuthorizationRequestContext extends OAuth2RequestContext { | ||
@@ -7,15 +7,12 @@ constructor(); | ||
} | ||
export declare class DeviceAccessTokenRequest extends OAuth2RequestContext { | ||
export declare class DeviceAuthorizationTokenRequestContext extends OAuth2RequestContext { | ||
constructor(deviceCode: string); | ||
} | ||
export declare function sendDeviceAuthorizationRequest<_ResponseBody extends DeviceAuthorizationResponseBody>(deviceAuthorizationEndpoint: string, context: DeviceAuthorizationRequestContext, options?: { | ||
signal?: AbortSignal; | ||
}): Promise<_ResponseBody>; | ||
export interface DeviceAuthorizationResponseBody { | ||
device_code: string; | ||
user_code: string; | ||
verification_uri: string; | ||
expires_in: number; | ||
interval?: number; | ||
verification_uri_complete?: string; | ||
export declare class DeviceAuthorizationRequestResult extends OAuth2RequestResult { | ||
deviceCode(): string; | ||
userCode(): string; | ||
verificationURI(): string; | ||
codesExpireInSeconds(): number; | ||
codesExpireAt(): Date; | ||
intervalSeconds(): number; | ||
} |
@@ -1,5 +0,5 @@ | ||
import { OAuth2RequestContext, OAuth2RequestError } from "./request.js"; | ||
import { OAuth2RequestContext, OAuth2RequestResult } from "./request.js"; | ||
export class DeviceAuthorizationRequestContext extends OAuth2RequestContext { | ||
constructor() { | ||
super(); | ||
super("POST"); | ||
} | ||
@@ -21,5 +21,5 @@ setScopes(...scopes) { | ||
} | ||
export class DeviceAccessTokenRequest extends OAuth2RequestContext { | ||
export class DeviceAuthorizationTokenRequestContext extends OAuth2RequestContext { | ||
constructor(deviceCode) { | ||
super(); | ||
super("POST"); | ||
this.body.set("grant_type", "urn:ietf:params:oauth:grant-type:device_code"); | ||
@@ -29,14 +29,39 @@ this.body.set("device_code", deviceCode); | ||
} | ||
export async function sendDeviceAuthorizationRequest(deviceAuthorizationEndpoint, context, options) { | ||
const request = context.toFetchRequest("POST", deviceAuthorizationEndpoint); | ||
const response = await fetch(request, { | ||
signal: options?.signal | ||
}); | ||
const result = await response.json(); | ||
if ("device_code" in result) { | ||
return result; | ||
export class DeviceAuthorizationRequestResult extends OAuth2RequestResult { | ||
deviceCode() { | ||
if ("device_code" in this.body && typeof this.body.device_code === "string") { | ||
return this.body.device_code; | ||
} | ||
throw new Error("Missing or invalid 'device_code' field"); | ||
} | ||
throw new OAuth2RequestError(result.error, request, context, response.headers, { | ||
description: result.error_description | ||
}); | ||
userCode() { | ||
if ("user_code" in this.body && typeof this.body.user_code === "string") { | ||
return this.body.user_code; | ||
} | ||
throw new Error("Missing or invalid 'user_code' field"); | ||
} | ||
verificationURI() { | ||
if ("verification_uri" in this.body && typeof this.body.verification_uri === "string") { | ||
return this.body.verification_uri; | ||
} | ||
throw new Error("Missing or invalid 'verification_uri' field"); | ||
} | ||
codesExpireInSeconds() { | ||
if ("expires_in" in this.body && typeof this.body.expires_in === "number") { | ||
return this.body.expires_in; | ||
} | ||
throw new Error("Missing or invalid 'expires_in' field"); | ||
} | ||
codesExpireAt() { | ||
return new Date(Date.now() + this.codesExpireInSeconds() * 1000); | ||
} | ||
intervalSeconds() { | ||
if ("interval" in this.body) { | ||
if (typeof this.body.interval === "number") { | ||
return this.body.interval; | ||
} | ||
throw new Error("Invalid 'interval' field"); | ||
} | ||
return 5; | ||
} | ||
} |
@@ -1,8 +0,6 @@ | ||
export { AuthorizationCodeAccessTokenRequestContext, AuthorizationCodeAuthorizationURL, generateCodeVerifier, generateState } from "./authorization-code.js"; | ||
export { DeviceAccessTokenRequest, DeviceAuthorizationRequestContext, sendDeviceAuthorizationRequest } from "./device-authorization.js"; | ||
export type { DeviceAuthorizationResponseBody } from "./device-authorization.js"; | ||
export { sendTokenRevocationRequest, TokenRevocationRequestContext } from "./token-revocation.js"; | ||
export { AuthorizationCodeTokenRequestContext, AuthorizationCodeAuthorizationURL, generateCodeVerifier, generateState } from "./authorization-code.js"; | ||
export { DeviceAuthorizationRequestContext, DeviceAuthorizationTokenRequestContext, DeviceAuthorizationRequestResult } from "./device-authorization.js"; | ||
export { TokenRevocationRequestContext, TokenType } from "./token-revocation.js"; | ||
export { RefreshRequestContext } from "./refresh-token.js"; | ||
export { OAuth2RequestContext, OAuth2RequestError } from "./request.js"; | ||
export { sendTokenRequest } from "./token.js"; | ||
export type { TokenResponseBody } from "./token.js"; | ||
export { OAuth2RequestContext, OAuth2RequestResult } from "./request.js"; | ||
export { TokenRequestResult } from "./token.js"; |
@@ -1,6 +0,6 @@ | ||
export { AuthorizationCodeAccessTokenRequestContext, AuthorizationCodeAuthorizationURL, generateCodeVerifier, generateState } from "./authorization-code.js"; | ||
export { DeviceAccessTokenRequest, DeviceAuthorizationRequestContext, sendDeviceAuthorizationRequest } from "./device-authorization.js"; | ||
export { sendTokenRevocationRequest, TokenRevocationRequestContext } from "./token-revocation.js"; | ||
export { AuthorizationCodeTokenRequestContext, AuthorizationCodeAuthorizationURL, generateCodeVerifier, generateState } from "./authorization-code.js"; | ||
export { DeviceAuthorizationRequestContext, DeviceAuthorizationTokenRequestContext, DeviceAuthorizationRequestResult } from "./device-authorization.js"; | ||
export { TokenRevocationRequestContext, TokenType } from "./token-revocation.js"; | ||
export { RefreshRequestContext } from "./refresh-token.js"; | ||
export { OAuth2RequestContext, OAuth2RequestError } from "./request.js"; | ||
export { sendTokenRequest } from "./token.js"; | ||
export { OAuth2RequestContext, OAuth2RequestResult } from "./request.js"; | ||
export { TokenRequestResult } from "./token.js"; |
import { OAuth2RequestContext } from "./request.js"; | ||
export class RefreshRequestContext extends OAuth2RequestContext { | ||
constructor(refreshToken) { | ||
super(); | ||
super("POST"); | ||
this.body.set("grant_type", "refresh_token"); | ||
@@ -6,0 +6,0 @@ this.body.set("refresh_token", refreshToken); |
export declare class OAuth2RequestContext { | ||
body: URLSearchParams; | ||
headers: Headers; | ||
constructor(); | ||
method: string; | ||
body: Map<string, string>; | ||
headers: Map<string, string>; | ||
constructor(method: string); | ||
setClientId(clientId: string): void; | ||
authenticateWithRequestBody(clientId: string, clientSecret: string): void; | ||
authenticateWithHTTPBasicAuth(clientId: string, clientSecret: string): void; | ||
toFetchRequest(method: string, url: string): Request; | ||
} | ||
export declare class OAuth2RequestError extends Error { | ||
request: Request; | ||
context: OAuth2RequestContext; | ||
description: string | null; | ||
responseHeaders: Headers; | ||
constructor(message: string, request: Request, context: OAuth2RequestContext, responseHeaders: Headers, options?: { | ||
description?: string; | ||
}); | ||
export declare class OAuth2RequestResult { | ||
body: object; | ||
constructor(body: object); | ||
hasErrorCode(): boolean; | ||
errorCode(): string; | ||
hasErrorDescription(): boolean; | ||
errorDescription(): string; | ||
hasErrorURI(): boolean; | ||
errorURI(): string; | ||
state(): string; | ||
} |
import { base64 } from "@oslojs/encoding"; | ||
export class OAuth2RequestContext { | ||
body = new URLSearchParams(); | ||
headers = new Headers(); | ||
constructor() { | ||
method; | ||
body = new Map(); | ||
headers = new Map(); | ||
constructor(method) { | ||
this.method = method; | ||
this.headers.set("Content-Type", "application/x-www-form-urlencoded"); | ||
@@ -21,22 +23,41 @@ this.headers.set("Accept", "application/json"); | ||
} | ||
toFetchRequest(method, url) { | ||
return new Request(url, { | ||
method, | ||
body: this.body, | ||
headers: this.headers | ||
}); | ||
} | ||
} | ||
export class OAuth2RequestError extends Error { | ||
request; | ||
context; | ||
description; | ||
responseHeaders; | ||
constructor(message, request, context, responseHeaders, options) { | ||
super(message); | ||
this.request = request; | ||
this.context = context; | ||
this.responseHeaders = responseHeaders; | ||
this.description = options?.description ?? null; | ||
export class OAuth2RequestResult { | ||
body; | ||
constructor(body) { | ||
this.body = body; | ||
} | ||
hasErrorCode() { | ||
return "error" in this.body && typeof this.body.error === "string"; | ||
} | ||
errorCode() { | ||
if ("error" in this.body && typeof this.body.error === "string") { | ||
return this.body.error; | ||
} | ||
throw new Error("Missing or invalid 'error' field"); | ||
} | ||
hasErrorDescription() { | ||
return "error_description" in this.body && typeof this.body.error_description === "string"; | ||
} | ||
errorDescription() { | ||
if ("error_description" in this.body && typeof this.body.error_description === "string") { | ||
return this.body.error_description; | ||
} | ||
throw new Error("Missing or invalid 'error_description' field"); | ||
} | ||
hasErrorURI() { | ||
return "error_uri" in this.body && typeof this.body.error_uri === "string"; | ||
} | ||
errorURI() { | ||
if ("error_uri" in this.body && typeof this.body.error_uri === "string") { | ||
return this.body.error_uri; | ||
} | ||
throw new Error("Missing or invalid 'error_uri' field"); | ||
} | ||
state() { | ||
if ("state" in this.body && typeof this.body.state === "string") { | ||
return this.body.state; | ||
} | ||
throw new Error("Missing or invalid 'state' field"); | ||
} | ||
} |
import { OAuth2RequestContext } from "./request.js"; | ||
export declare function sendTokenRevocationRequest(tokenRevocationEndpoint: string, context: TokenRevocationRequestContext, options?: { | ||
signal?: AbortSignal; | ||
}): Promise<void>; | ||
export declare enum TokenType { | ||
AccessToken = 0, | ||
RefreshToken = 1 | ||
} | ||
export declare class TokenRevocationRequestContext extends OAuth2RequestContext { | ||
constructor(token: string); | ||
setTokenTypeHint(tokenType: "access_token" | "refresh_token"): void; | ||
setTokenTypeHint(tokenType: TokenType): void; | ||
} |
@@ -1,23 +0,20 @@ | ||
import { OAuth2RequestContext, OAuth2RequestError } from "./request.js"; | ||
export async function sendTokenRevocationRequest(tokenRevocationEndpoint, context, options) { | ||
const request = context.toFetchRequest("POST", tokenRevocationEndpoint); | ||
const response = await fetch(request, { | ||
signal: options?.signal | ||
}); | ||
if (response.status === 200) { | ||
return; | ||
} | ||
const result = await response.json(); | ||
throw new OAuth2RequestError(result.error, request, context, response.headers, { | ||
description: result.error_description | ||
}); | ||
} | ||
import { OAuth2RequestContext } from "./request.js"; | ||
export var TokenType; | ||
(function (TokenType) { | ||
TokenType[TokenType["AccessToken"] = 0] = "AccessToken"; | ||
TokenType[TokenType["RefreshToken"] = 1] = "RefreshToken"; | ||
})(TokenType || (TokenType = {})); | ||
export class TokenRevocationRequestContext extends OAuth2RequestContext { | ||
constructor(token) { | ||
super(); | ||
super("POST"); | ||
this.body.set("token", token); | ||
} | ||
setTokenTypeHint(tokenType) { | ||
this.body.set("token_type_hint", tokenType); | ||
if (tokenType === TokenType.AccessToken) { | ||
this.body.set("token_type_hint", "access_token"); | ||
} | ||
else if (tokenType === TokenType.RefreshToken) { | ||
this.body.set("token_type_hint", "refresh_token"); | ||
} | ||
} | ||
} |
@@ -1,15 +0,11 @@ | ||
import type { OAuth2RequestContext } from "./request.js"; | ||
export interface TokenResponseBody { | ||
access_token: string; | ||
token_type: string; | ||
expires_in?: number; | ||
refresh_token?: string; | ||
scope?: string; | ||
import { OAuth2RequestResult } from "./request.js"; | ||
export declare class TokenRequestResult extends OAuth2RequestResult { | ||
tokenType(): string; | ||
accessToken(): string; | ||
accessTokenExpiresInSeconds(): number; | ||
accessTokenExpiresAt(): Date; | ||
hasRefreshToken(): boolean; | ||
refreshToken(): string; | ||
refreshTokenExpiresInSeconds(): number; | ||
refreshTokenExpiresAt(): Date; | ||
} | ||
export interface TokenErrorResponseBody { | ||
error: string; | ||
error_description?: string; | ||
} | ||
export declare function sendTokenRequest<_TokenResponseBody extends TokenResponseBody>(tokenEndpoint: string, context: OAuth2RequestContext, options?: { | ||
signal?: AbortSignal; | ||
}): Promise<_TokenResponseBody>; |
@@ -1,14 +0,43 @@ | ||
import { OAuth2RequestError } from "./request.js"; | ||
export async function sendTokenRequest(tokenEndpoint, context, options) { | ||
const request = context.toFetchRequest("POST", tokenEndpoint); | ||
const response = await fetch(request, { | ||
signal: options?.signal | ||
}); | ||
const result = await response.json(); | ||
if ("access_token" in result) { | ||
return result; | ||
import { OAuth2RequestResult } from "./request.js"; | ||
export class TokenRequestResult extends OAuth2RequestResult { | ||
tokenType() { | ||
if ("token_type" in this.body && typeof this.body.token_type === "string") { | ||
return this.body.token_type; | ||
} | ||
throw new Error("Missing or invalid 'token_type' field"); | ||
} | ||
throw new OAuth2RequestError(result.error, request, context, response.headers, { | ||
description: result.error_description | ||
}); | ||
accessToken() { | ||
if ("access_token" in this.body && typeof this.body.access_token === "string") { | ||
return this.body.access_token; | ||
} | ||
throw new Error("Missing or invalid 'access_token' field"); | ||
} | ||
accessTokenExpiresInSeconds() { | ||
if ("expires_in" in this.body && typeof this.body.expires_in === "number") { | ||
return this.body.expires_in; | ||
} | ||
throw new Error("Missing or invalid 'expires_in' field"); | ||
} | ||
accessTokenExpiresAt() { | ||
return new Date(Date.now() + this.accessTokenExpiresInSeconds() * 1000); | ||
} | ||
hasRefreshToken() { | ||
return "refresh_token" in this.body && typeof this.body.refresh_token === "string"; | ||
} | ||
refreshToken() { | ||
if ("refresh_token" in this.body && typeof this.body.refresh_token === "string") { | ||
return this.body.refresh_token; | ||
} | ||
throw new Error("Missing or invalid 'refresh_token' field"); | ||
} | ||
refreshTokenExpiresInSeconds() { | ||
if ("refresh_token_expires_in" in this.body && | ||
typeof this.body.refresh_token_expires_in === "number") { | ||
return this.body.refresh_token_expires_in; | ||
} | ||
throw new Error("Missing or invalid 'refresh_token_expires_in' field"); | ||
} | ||
refreshTokenExpiresAt() { | ||
return new Date(Date.now() + this.refreshTokenExpiresInSeconds() * 1000); | ||
} | ||
} |
{ | ||
"name": "@oslojs/oauth2", | ||
"type": "module", | ||
"version": "0.1.2", | ||
"version": "0.2.0", | ||
"description": "A runtime-agnostic OAuth 2.0 library", | ||
@@ -6,0 +6,0 @@ "main": "dist/index.js", |
@@ -14,9 +14,27 @@ # @oslojs/oauth2 | ||
```ts | ||
import { AuthorizationCodeAccessTokenRequestContext, sendTokenRequest } from "@oslojs/oauth2"; | ||
import { AuthorizationCodeTokenRequestContext, TokenRequestResult } from "@oslojs/oauth2"; | ||
const context = new AuthorizationCodeAccessTokenRequestContext(code); | ||
const context = new AuthorizationCodeTokenRequestContext(code); | ||
context.authenticateWithHTTPBasicAuth(clientId, clientSecret); | ||
context.setRedirectURI("https://my-app.com/login/callback"); | ||
const tokens = await sendTokenRequest(tokenEndpoint, context); | ||
const accessToken = tokens.access_token; | ||
const body = new URLSearchParams(); | ||
for (const [key, value] of context.body.entries()) { | ||
body.set(key, value); | ||
} | ||
const response = await fetch("https://github.com/login/oauth/access_token", { | ||
method: context.method, | ||
body, | ||
headers: new Headers(context.headers) | ||
}); | ||
const data = await response.json(); | ||
const result = new TokenRequestResult(data); | ||
if (result.hasErrorCode()) { | ||
const error = result.errorCode(); | ||
} else { | ||
const accessToken = result.accessToken(); | ||
const accessTokenExpiresAt = result.accessTokenExpiresAt(); | ||
const refreshToken = result.refreshToken(); | ||
} | ||
``` | ||
@@ -23,0 +41,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
18390
367
64
0