From 383aa2d04acce830adaae7adc27a159b8b383d96 Mon Sep 17 00:00:00 2001 From: pilcrow Date: Thu, 28 Mar 2024 13:48:42 +0900 Subject: [PATCH] Add VK provider (#86) * add vk provider * fix exports --- .changesets/hu3zn.minor.md | 1 + docs/malta.config.json | 1 + docs/pages/providers/vk.md | 35 ++++++++++++++++++++++ src/index.ts | 2 ++ src/providers/vk.ts | 59 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 98 insertions(+) create mode 100644 .changesets/hu3zn.minor.md create mode 100644 docs/pages/providers/vk.md create mode 100644 src/providers/vk.ts diff --git a/.changesets/hu3zn.minor.md b/.changesets/hu3zn.minor.md new file mode 100644 index 00000000..70aaebc1 --- /dev/null +++ b/.changesets/hu3zn.minor.md @@ -0,0 +1 @@ +Add VK provider. \ No newline at end of file diff --git a/docs/malta.config.json b/docs/malta.config.json index 4ec3924d..3ee71e5b 100644 --- a/docs/malta.config.json +++ b/docs/malta.config.json @@ -49,6 +49,7 @@ ["Tumblr", "/providers/tumblr"], ["Twitch", "/providers/twitch"], ["Twitter", "/providers/twitter"], + ["VK", "/providers/vk"], ["WorkOS", "/providers/workos"], ["Yahoo", "/providers/yahoo"], ["Yandex", "/providers/yandex"], diff --git a/docs/pages/providers/vk.md b/docs/pages/providers/vk.md new file mode 100644 index 00000000..ec278764 --- /dev/null +++ b/docs/pages/providers/vk.md @@ -0,0 +1,35 @@ +--- +title: "VK" +--- + +# VK + +For usage, see [OAuth 2.0 provider](/guides/oauth2). + +```ts +import { VK } from "arctic"; + +const vk = new VK(clientId, clientSecret, redirectURI); +``` + +```ts +const url: URL = await vk.createAuthorizationURL(state, { + // optional + scopes +}); +const tokens: VKTokens = await vk.validateAuthorizationCode(code); +``` + +## Get user profile + +Use the [`users.get` endpoint`](https://dev.vk.com/en/method/users.get). + +```ts +const tokens = await vk.validateAuthorizationCode(code); +const response = await fetch("https://api.vk.com/method/users.get", { + headers: { + Authorization: `Bearer ${tokens.accessToken}` + } +}); +const user = await response.json(); +``` diff --git a/src/index.ts b/src/index.ts index 0b6a7a39..4c747799 100644 --- a/src/index.ts +++ b/src/index.ts @@ -32,6 +32,7 @@ export { Strava } from "./providers/strava.js"; export { Tumblr } from "./providers/tumblr.js"; export { Twitch } from "./providers/twitch.js"; export { Twitter } from "./providers/twitter.js"; +export { VK } from "./providers/vk.js"; export { WorkOS } from "./providers/workos.js"; export { Yahoo } from "./providers/yahoo.js"; export { Yandex } from "./providers/yandex.js"; @@ -74,6 +75,7 @@ export type { StravaTokens } from "./providers/strava.js"; export type { TumblrTokens } from "./providers/tumblr.js"; export type { TwitchTokens } from "./providers/twitch.js"; export type { TwitterTokens } from "./providers/twitter.js"; +export type { VKTokens } from "./providers/vk.js"; export type { WorkOSTokens } from "./providers/workos.js"; export type { YahooTokens } from "./providers/yahoo.js"; export type { YandexTokens } from "./providers/yandex.js"; diff --git a/src/providers/vk.ts b/src/providers/vk.ts new file mode 100644 index 00000000..f81a8fba --- /dev/null +++ b/src/providers/vk.ts @@ -0,0 +1,59 @@ +import { OAuth2Client } from "oslo/oauth2"; +import { TimeSpan, createDate } from "oslo"; + +import type { OAuth2Provider } from "../index.js"; + +const authorizeEndpoint = "https://oauth.vk.com/authorize"; +const tokenEndpoint = "https://oauth.vk.com/access_token"; + +export class VK implements OAuth2Provider { + private client: OAuth2Client; + private clientSecret: string; + + constructor(clientId: string, clientSecret: string, redirectURI: string) { + this.client = new OAuth2Client(clientId, authorizeEndpoint, tokenEndpoint, { + redirectURI + }); + this.clientSecret = clientSecret; + } + + public async createAuthorizationURL( + state: string, + options?: { + scopes?: string[]; + } + ): Promise { + return await this.client.createAuthorizationURL({ + state, + scopes: options?.scopes ?? [] + }); + } + + public async validateAuthorizationCode(code: string): Promise { + const result = await this.client.validateAuthorizationCode(code, { + authenticateWith: "request_body", + credentials: this.clientSecret + }); + const tokens: VKTokens = { + accessToken: result.access_token, + accessTokenExpiresAt: createDate(new TimeSpan(result.expires_in, "s")), + userId: result.user_id, + email: result.email ?? null + }; + return tokens; + } +} + +interface TokenResponseBody { + access_token: string; + expires_in: number; + user_id: string; + email?: string; +} + +export interface VKTokens { + accessToken: string; + accessTokenExpiresAt: Date; + userId: string; + email: string | null; +}