diff --git a/packages/core/src/api/index.ts b/packages/core/src/api/index.ts index 236b36684..924f00d0b 100644 --- a/packages/core/src/api/index.ts +++ b/packages/core/src/api/index.ts @@ -4,6 +4,7 @@ import { ChannelsAPI } from './channel.js'; import { GuildsAPI } from './guild.js'; import { InteractionsAPI } from './interactions.js'; import { InvitesAPI } from './invite.js'; +import { OAuth2API } from './oauth2.js'; import { RoleConnectionsAPI } from './roleConnections.js'; import { StickersAPI } from './sticker.js'; import { ThreadsAPI } from './thread.js'; @@ -16,6 +17,7 @@ export * from './channel.js'; export * from './guild.js'; export * from './interactions.js'; export * from './invite.js'; +export * from './oauth2.js'; export * from './roleConnections.js'; export * from './sticker.js'; export * from './thread.js'; @@ -34,6 +36,8 @@ export class API { public readonly invites: InvitesAPI; + public readonly oauth2: OAuth2API; + public readonly roleConnections: RoleConnectionsAPI; public readonly stickers: StickersAPI; @@ -52,6 +56,7 @@ export class API { this.guilds = new GuildsAPI(rest); this.invites = new InvitesAPI(rest); this.roleConnections = new RoleConnectionsAPI(rest); + this.oauth2 = new OAuth2API(rest); this.stickers = new StickersAPI(rest); this.threads = new ThreadsAPI(rest); this.users = new UsersAPI(rest); diff --git a/packages/core/src/api/oauth2.ts b/packages/core/src/api/oauth2.ts new file mode 100644 index 000000000..b4bf7361b --- /dev/null +++ b/packages/core/src/api/oauth2.ts @@ -0,0 +1,97 @@ +import { URL } from 'node:url'; +import type { REST } from '@discordjs/rest'; +import { makeURLSearchParams } from '@discordjs/rest'; +import { + Routes, + RouteBases, + type RESTOAuth2AuthorizationQuery, + type RESTPostOAuth2RefreshTokenURLEncodedData, + type RESTPostOAuth2RefreshTokenResult, + type RESTPostOAuth2ClientCredentialsURLEncodedData, + type RESTPostOAuth2ClientCredentialsResult, + type RESTGetAPIOAuth2CurrentAuthorizationResult, + type RESTGetAPIOAuth2CurrentApplicationResult, + type RESTPostOAuth2AccessTokenURLEncodedData, + type RESTPostOAuth2AccessTokenResult, +} from 'discord-api-types/v10'; + +export class OAuth2API { + public constructor(private readonly rest: REST) {} + + /** + * Creates an OAuth2 authorization URL given the options + * + * @see {@link https://discord.com/developers/docs/topics/oauth2#authorization-code-grant-authorization-url-example} + * @param options - The options for creating the authorization URL + */ + public generateAuthorizationURL(options: RESTOAuth2AuthorizationQuery) { + const url = new URL(`${RouteBases.api}${Routes.oauth2Authorization()}`); + url.search = makeURLSearchParams(options).toString(); + return url.toString(); + } + + /** + * Performs an OAuth2 token exchange, giving you an access token + * + * @see {@link https://discord.com/developers/docs/topics/oauth2#authorization-code-grant-access-token-exchange-example} + * @param options - The options for the token exchange request + */ + public async tokenExchange(options: RESTPostOAuth2AccessTokenURLEncodedData) { + return this.rest.post(Routes.oauth2TokenExchange(), { + body: makeURLSearchParams(options), + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + }) as Promise; + } + + /** + * Refreshes an OAuth2 access token, giving you a new one + * + * @see {@link https://discord.com/developers/docs/topics/oauth2#authorization-code-grant-refresh-token-exchange-example} + * @param options - The options for the refresh token request + */ + public async refreshToken(options: RESTPostOAuth2RefreshTokenURLEncodedData) { + return this.rest.post(Routes.oauth2TokenExchange(), { + body: makeURLSearchParams(options), + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + }) as Promise; + } + + /** + * Fetches the bearer token for the current application + * + * @remarks + * This is primarily used for testing purposes + * @see {@link https://discord.com/developers/docs/topics/oauth2#client-credentials-grant} + * @param options - The options for the client credentials grant request + */ + public async getToken(options: RESTPostOAuth2ClientCredentialsURLEncodedData) { + return this.rest.post(Routes.oauth2TokenExchange(), { + body: makeURLSearchParams(options), + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + }) as Promise; + } + + /** + * Fetches the current bot's application information + * + * @see {@link https://discord.com/developers/docs/topics/oauth2#get-current-bot-application-information} + */ + public async getCurrentBotApplicationInformation() { + return this.rest.get(Routes.oauth2CurrentApplication()) as Promise; + } + + /** + * Fetches the current authorization information + * + * @see {@link https://discord.com/developers/docs/topics/oauth2#get-current-authorization-information} + */ + public async getCurrentAuthorizationInformation() { + return this.rest.get(Routes.oauth2CurrentAuthorization()) as Promise; + } +}