-
-
Notifications
You must be signed in to change notification settings - Fork 87
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
6ab0262
commit d6f236a
Showing
5 changed files
with
175 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |