Skip to content

Commit

Permalink
Add Roblox provider (#88)
Browse files Browse the repository at this point in the history
* add roblox provider

* format
  • Loading branch information
pilcrowonpaper authored Mar 28, 2024
1 parent 383aa2d commit ba16989
Show file tree
Hide file tree
Showing 10 changed files with 140 additions and 8 deletions.
1 change: 1 addition & 0 deletions .changesets/94a8i.minor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add Roblox provider.
2 changes: 1 addition & 1 deletion .changesets/hu3zn.minor.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Add VK provider.
Add VK provider.
2 changes: 1 addition & 1 deletion .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
github: pilcrowOnPaper
github: pilcrowOnPaper
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ We welcome all contributions to the docs, especially grammar fixes. Arctic uses

## Contributing to the source code

We are open to most contributions, but please open a new issue before creating a pull request, especially for new features. It's likely your PR will be rejected if not. We have intentionally limited the scope of the project and we would like to keep the package lean.
We are open to most contributions, but please open a new issue before creating a pull request, especially for new features. It's likely your PR will be rejected if not. We have intentionally limited the scope of the project and we would like to keep the package lean.

### Set up

Expand Down
1 change: 1 addition & 0 deletions docs/malta.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
["osu!", "/providers/osu"],
["Patreon", "/providers/patreon"],
["Reddit", "/providers/reddit"],
["Roblox", "/providers/roblox"],
["Salesforce", "/providers/salesforce"],
["Slack", "/providers/slack"],
["Spotify", "/providers/spotify"],
Expand Down
46 changes: 46 additions & 0 deletions docs/pages/providers/roblox.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
title: "Roblox"
---

# Roblox

Implements OpenID Connect. For confidential clients and not public clients.

For usage, see [OAuth 2.0 provider with PKCE](/guides/oauth2-pkce).

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

const roblox = new Roblox(clientId, clientSecret, redirectURI);
```

```ts
const url: URL = await roblox.createAuthorizationURL(state, codeVerifier, {
// optional
scopes // "openid" always included
});
const tokens: RobloxTokens = await roblox.validateAuthorizationCode(code, codeVerifier);
const tokens: RobloxTokens = await roblox.refreshAccessToken(refreshToken);
```

## Get user profile

Add the `profile` scope.

```ts
const url = await roblox.createAuthorizationURL(state, codeVerifier, {
scopes: ["profile"]
});
```

Parse the ID token or use the [`userinfo` endpoint](https://create.roblox.com/docs/cloud/reference/oauth2#get-v1userinfo).

```ts
const tokens = await roblox.validateAuthorizationCode(code, codeVerifier);
const response = await fetch("https://apis.roblox.com/oauth/v1/userinfo", {
headers: {
Authorization: `Bearer ${tokens.accessToken}`
}
});
const user = await response.json();
```
2 changes: 1 addition & 1 deletion docs/pages/providers/yandex.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,4 @@ const response = await fetch("https://api.tracker.yandex.net/v2/myself", {
}
});
const user = await response.json();
```
```
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export { Okta } from "./providers/okta.js";
export { Osu } from "./providers/osu.js";
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 { Slack } from "./providers/slack.js";
export { Spotify } from "./providers/spotify.js";
Expand Down Expand Up @@ -68,6 +69,7 @@ export type { OktaTokens } from "./providers/okta.js";
export type { OsuTokens } from "./providers/osu.js";
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 { SlackTokens } from "./providers/slack.js";
export type { SpotifyTokens } from "./providers/spotify.js";
Expand Down
78 changes: 78 additions & 0 deletions src/providers/roblox.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { TimeSpan, createDate } from "oslo";
import { OAuth2Client } from "oslo/oauth2";

import type { OAuth2ProviderWithPKCE } from "../index.js";

const authorizeEndpoint = "https://apis.roblox.com/oauth/v1/authorize";
const tokenEndpoint = "https://apis.roblox.com/oauth/v1/token";

export class Roblox implements OAuth2ProviderWithPKCE {
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,
codeVerifier: string,
options?: {
scopes?: string[];
}
): Promise<URL> {
const scopes = options?.scopes ?? [];
return await this.client.createAuthorizationURL({
state,
codeVerifier,
scopes: [...scopes, "openid"]
});
}

public async validateAuthorizationCode(
code: string,
codeVerifier: string
): Promise<RobloxTokens> {
const result = await this.client.validateAuthorizationCode<TokenResponseBody>(code, {
credentials: this.clientSecret,
codeVerifier
});
const tokens: RobloxTokens = {
accessToken: result.access_token,
refreshToken: result.refresh_token,
accessTokenExpiresAt: createDate(new TimeSpan(result.expires_in, "s")),
idToken: result.id_token
};
return tokens;
}

public async refreshAccessToken(refreshToken: string): Promise<RobloxTokens> {
const result = await this.client.refreshAccessToken<TokenResponseBody>(refreshToken, {
credentials: this.clientSecret
});
const tokens: RobloxTokens = {
accessToken: result.access_token,
refreshToken: result.refresh_token,
accessTokenExpiresAt: createDate(new TimeSpan(result.expires_in, "s")),
idToken: result.id_token
};
return tokens;
}
}

interface TokenResponseBody {
access_token: string;
refresh_token: string;
expires_in: number;
id_token: string;
}

export interface RobloxTokens {
accessToken: string;
refreshToken: string;
accessTokenExpiresAt: Date;
idToken: string;
}
12 changes: 8 additions & 4 deletions src/providers/yandex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@ export class Yandex implements OAuth2Provider {
private client: OAuth2Client;
private clientSecret: string;

constructor(clientId: string, clientSecret: string, options?: {
redirectURI: string
}) {
constructor(
clientId: string,
clientSecret: string,
options?: {
redirectURI: string;
}
) {
this.client = new OAuth2Client(clientId, authorizeEndpoint, tokenEndpoint, {
redirectURI: options?.redirectURI
});
Expand All @@ -27,7 +31,7 @@ export class Yandex implements OAuth2Provider {
): Promise<URL> {
return await this.client.createAuthorizationURL({
state,
scopes: options?.scopes ?? [],
scopes: options?.scopes ?? []
});
}

Expand Down

0 comments on commit ba16989

Please sign in to comment.