Skip to content

Commit

Permalink
Add Shikimori provider (#95)
Browse files Browse the repository at this point in the history
* feat: implement shikimori provider

* docs: add docs for shikimori provider

* ShikimoriTokens -> TokenResponseBody and remove export, ShikimoriTokens interface for validateAuthorizationCode

* ShikimoriTokens for refreshAccessToken

* update readme and docs
  • Loading branch information
neverlane authored Apr 26, 2024
1 parent ca4add2 commit c389f5d
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 0 deletions.
1 change: 1 addition & 0 deletions .changesets/15lio.minor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add Shikimori provider.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ Arctic does not strictly follow semantic versioning. While we aim to only introd
- Reddit
- Roblox
- Salesforce
- Shikimori
- Slack
- Spotify
- Strava
Expand Down
1 change: 1 addition & 0 deletions docs/malta.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
["Reddit", "/providers/reddit"],
["Roblox", "/providers/roblox"],
["Salesforce", "/providers/salesforce"],
["Shikimori", "/providers/shikimori"],
["Slack", "/providers/slack"],
["Spotify", "/providers/spotify"],
["Strava", "/providers/strava"],
Expand Down
21 changes: 21 additions & 0 deletions docs/pages/providers/shikimori.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
title: "Shikimori"
---

# Shikimori

For usage, see [OAuth 2.0 provider](/guides/oauth2).

```ts
import { Shikimori } from "arctic";

const shikimori = new Shikimori(clientId, clientSecret, redirectURI);
```

```ts
import type { ShikimoriTokens } from "arctic";

const url: URL = await shikimori.createAuthorizationURL(state);
const tokens: ShikimoriTokens = await shikimori.validateAuthorizationCode(code);
const refreshedTokens: ShikimoriTokens = await shikimori.refreshAccessToken(refreshToken);
```
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export { Patreon } from "./providers/patreon.js";
export { Reddit } from "./providers/reddit.js";
export { Roblox } from "./providers/roblox.js";
export { Salesforce } from "./providers/salesforce.js";
export { Shikimori } from "./providers/shikimori.js";
export { Slack } from "./providers/slack.js";
export { Spotify } from "./providers/spotify.js";
export { Strava } from "./providers/strava.js";
Expand Down Expand Up @@ -77,6 +78,7 @@ export type { PatreonTokens } from "./providers/patreon.js";
export type { RedditTokens } from "./providers/reddit.js";
export type { RobloxTokens } from "./providers/roblox.js";
export type { SalesforceTokens } from "./providers/salesforce.js";
export type { ShikimoriTokens } from "./providers/shikimori.js";
export type { SlackTokens } from "./providers/slack.js";
export type { SpotifyTokens } from "./providers/spotify.js";
export type { StravaTokens } from "./providers/strava.js";
Expand Down
62 changes: 62 additions & 0 deletions src/providers/shikimori.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { OAuth2Client } from "oslo/oauth2";
import type { OAuth2Provider } from "../index.js";

const authorizeEndpoint = "https://shikimori.one/oauth/authorize";
const tokenEndpoint = "https://shikimori.one/oauth/token";

export class Shikimori 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;
}

createAuthorizationURL(state: string, scopes?: string[]): Promise<URL> {
return this.client.createAuthorizationURL({ state, scopes });
}

async validateAuthorizationCode(code: string): Promise<ShikimoriTokens> {
const result = await this.client.validateAuthorizationCode<TokenResponseBody>(code, {
authenticateWith: "request_body",
credentials: this.clientSecret
});

return {
accessToken: result.access_token,
refreshToken: result.refresh_token,
accessTokenExpiresAt: new Date((result.created_at + result.expires_in) * 1000),
};
}

async refreshAccessToken(refreshToken: string): Promise<ShikimoriTokens> {
const result = await this.client.refreshAccessToken<TokenResponseBody>(refreshToken, {
authenticateWith: "request_body",
credentials: this.clientSecret
});

return {
accessToken: result.access_token,
refreshToken: result.refresh_token,
accessTokenExpiresAt: new Date((result.created_at + result.expires_in) * 1000),
};
}
}

interface TokenResponseBody {
access_token: string;
token_type: string;
expires_in: number;
refresh_token: string;
scope: string;
created_at: number;
}

export interface ShikimoriTokens {
accessToken: string;
refreshToken: string;
accessTokenExpiresAt: Date;
}

0 comments on commit c389f5d

Please sign in to comment.