Skip to content

Commit

Permalink
add bungie provider
Browse files Browse the repository at this point in the history
  • Loading branch information
pilcrowonpaper committed Sep 16, 2024
1 parent 6ab0262 commit d6f236a
Show file tree
Hide file tree
Showing 5 changed files with 175 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ Arctic does not strictly follow semantic versioning. While we aim to only introd
- Authentik
- Bitbucket
- Box
- Bungie
- Coinbase
- Discord
- Dribbble
Expand Down
1 change: 1 addition & 0 deletions docs/malta.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
["Authentik", "/providers/authentik"],
["Bitbucket", "/providers/bitbucket"],
["Box", "/providers/box"],
["Bungie", "/providers/bungie"],
["Coinbase", "/providers/coinbase"],
["Discord", "/providers/discord"],
["Dribbble", "/providers/dribbble"],
Expand Down
109 changes: 109 additions & 0 deletions docs/pages/providers/bungie.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
---
title: "Bungie"
---

# Bungie

OAuth 2.0 provider for Bungie.

Also see the [OAuth 2.0](/guides/oauth2) guide.

## Initialization

Pass a client secret for confidential clients.

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

const bungie = new Bungie(clientId, clientSecret, redirectURI);
const bungie = new Bungie(clientId, null, redirectURI);
```

## Create authorization URL

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

const state = generateState();
const url = bungie.createAuthorizationURL(state);
```

## Validate authorization code

`validateAuthorizationCode()` will either return an [`OAuth2Tokens`](/reference/main/OAuth2Tokens), or throw one of [`OAuth2RequestError`](/reference/main/OAuth2RequestError), [`ArcticFetchError`](/reference/main/ArcticFetchError), or a standard `Error` (parse errors).

```ts
import { OAuth2RequestError, ArcticFetchError } from "arctic";

try {
const tokens = await bungie.validateAuthorizationCode(code);
const accessToken = tokens.accessToken();
} catch (e) {
if (e instanceof OAuth2RequestError) {
// Invalid authorization code, credentials, or redirect URI
const code = e.code;
// ...
}
if (e instanceof ArcticFetchError) {
// Failed to call `fetch()`
const cause = e.cause;
// ...
}
// Parse error
}
```

Refresh tokens are only provided for confidential clients.

```ts
const tokens = await bungie.validateAuthorizationCode(code);
const accessToken = tokens.accessToken();
const accessTokenExpiresAt = tokens.accessTokenExpiresAt();
const refreshToken = tokens.refreshToken();
```

The refresh token expiration is returned as `refresh_expires_in`.

```ts
const tokens = await github.validateAuthorizationCode(code);
if ("refresh_expires_in" in tokens.data && typeof tokens.data.refresh_expires_in === "number") {
const refreshTokenExpiresIn = tokens.data.refresh_expires_in;
}
```

## Refresh access tokens

For confidential clients, use `refreshAccessToken()` to get a new access token using a refresh token. The behavior is identical to `validateAuthorizationCode()`.

```ts
import { OAuth2RequestError, ArcticFetchError } from "arctic";

try {
const tokens = await bungie.refreshAccessToken(accessToken);
const accessToken = tokens.accessToken();
const accessTokenExpiresAt = tokens.accessTokenExpiresAt();
const refreshToken = tokens.refreshToken();
} catch (e) {
if (e instanceof OAuth2RequestError) {
// Invalid authorization code, credentials, or redirect URI
}
if (e instanceof ArcticFetchError) {
// Failed to call `fetch()`
}
// Parse error
}
```

## Get user profile

Add the `account` scope and use the [`GetCurrentBungieNetUser` endpoint](https://destinydevs.github.io/BungieNetPlatform/docs/services/User/User-GetCurrentBungieNetUser).

```ts
const response = await fetch("https://www.bungie.net/Platform/User/GetCurrentBungieNetUser", {
headers: {
Authorization: `Bearer ${accessToken}`,
"X-API-Key": apiKey
}
});
const emails = await response.json();
```
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export { Auth0 } from "./providers/auth0.js";
export { Authentik } from "./providers/authentik.js";
export { Bitbucket } from "./providers/bitbucket.js";
export { Box } from "./providers/box.js";
export { Bungie } from "./providers/bungie.js";
export { Coinbase } from "./providers/coinbase.js";
export { Discord } from "./providers/discord.js";
export { Dribbble } from "./providers/dribbble.js";
Expand Down
63 changes: 63 additions & 0 deletions src/providers/bungie.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { createOAuth2Request, encodeBasicCredentials, sendTokenRequest } from "../request.js";

import type { OAuth2Tokens } from "../oauth2.js";

const authorizationEndpoint = "https://www.bungie.net/en/oauth/authorize";
const tokenEndpoint = "https://www.bungie.net/platform/app/oauth/token";

export class Bungie {
private clientId: string;
private clientSecret: string | null;
private redirectURI: string;

constructor(clientId: string, clientSecret: string | null, redirectURI: string) {
this.clientId = clientId;
this.clientSecret = clientSecret;
this.redirectURI = redirectURI;
}

public createAuthorizationURL(state: string): URL {
const url = new URL(authorizationEndpoint);
url.searchParams.set("response_type", "code");
url.searchParams.set("client_id", this.clientId);
url.searchParams.set("state", state);
url.searchParams.set("redirect_uri", this.redirectURI);
return url;
}

public async validateAuthorizationCode(code: string): Promise<OAuth2Tokens> {
let request: Request;
if (this.clientSecret !== null) {
const body = new URLSearchParams();
body.set("grant_type", "authorization_code");
body.set("code", code);
body.set("redirect_uri", this.redirectURI);
request = createOAuth2Request(tokenEndpoint, body);
const encodedCredentials = encodeBasicCredentials(this.clientId, this.clientSecret);
request.headers.set("Authorization", `Basic ${encodedCredentials}`);
} else {
const body = new URLSearchParams();
body.set("client_id", this.clientId);
body.set("grant_type", "authorization_code");
body.set("code", code);
body.set("redirect_uri", this.redirectURI);
request = createOAuth2Request(tokenEndpoint, body);
}
const tokens = await sendTokenRequest(request);
return tokens;
}

public async refreshAccessToken(refreshToken: string): Promise<OAuth2Tokens> {
const body = new URLSearchParams();
body.set("grant_type", "refresh_token");
body.set("refresh_token", refreshToken);
const request = createOAuth2Request(tokenEndpoint, body);
if (this.clientSecret === null) {
throw new Error("Refresh tokens can only be used in confidential clients");
}
const encodedCredentials = encodeBasicCredentials(this.clientId, this.clientSecret);
request.headers.set("Authorization", `Basic ${encodedCredentials}`);
const tokens = await sendTokenRequest(request);
return tokens;
}
}

0 comments on commit d6f236a

Please sign in to comment.