Skip to content

Commit

Permalink
Add Spotify OAuth provider (#603)
Browse files Browse the repository at this point in the history
Co-authored-by: pilcrowOnPaper <[email protected]>
  • Loading branch information
msonnberger and pilcrowonpaper authored Jun 23, 2023
1 parent 5e8186d commit fb796bc
Show file tree
Hide file tree
Showing 19 changed files with 336 additions and 48 deletions.
6 changes: 6 additions & 0 deletions .auri/$6ayq7wdu.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
package: "integration-oauth" # package name
type: "minor" # "major", "minor", "patch"
---

Add Spotify OAuth provider
155 changes: 155 additions & 0 deletions documentation-v2/content/oauth/providers/spotify.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
---
order:: 0
title: "Spotify"
description: "Learn about using the Spotify provider in Lucia OAuth integration"
---

OAuth integration for Spotify. Refer to [Spotify OAuth documentation](https://developer.spotify.com/documentation/web-api/concepts/apps) for getting the required credentials. Provider id is `spotify`.

```ts
import { spotify } from "@lucia-auth/oauth/providers";
import { auth } from "./lucia.js";

const spotifyAuth = spotify(auth, config);
```

## `spotify()`

```ts
const spotify: (
auth: Auth,
configs: {
clientId: string;
clientSecret: string;
redirectUri: string;
scope?: string[];
showDialog?: boolean;
}
) => SpotifyProvider;
```

##### Parameters

| name | type | description | optional |
| ---------------------- | ------------------------------------------ | --------------------------------------------- | :------: |
| `auth` | [`Auth`](/reference/lucia/interfaces/auth) | Lucia instance | |
| `configs.clientId` | `string` | Spotify OAuth app client id | |
| `configs.clientSecret` | `string` | Spotify OAuth app client secret | |
| `configs.redirectUri` | `string` | one of the authorized redirect URIs | |
| `configs.scope` | `string[]` | an array of scopes ||
| `configs.showDialog` | `boolean` | force the user to approve the app every time. ||

##### Returns

| type | description |
| ------------------------------------- | ---------------- |
| [`SpotifyProvider`](#spotifyprovider) | Spotify provider |

## Interfaces

### `SpotifyProvider`

Satisfies [`OAuthProvider`](/reference/oauth/interfaces#oauthprovider).

#### `getAuthorizationUrl()`

Returns the authorization url for user redirection and a state for storage. The state should be stored in a cookie and validated on callback.

```ts
const getAuthorizationUrl: () => Promise<[url: URL, state: string]>;
```

##### Returns

| name | type | description |
| ------- | -------- | -------------------- |
| `url` | `URL` | authorize url |
| `state` | `string` | state parameter used |

#### `validateCallback()`

Validates the callback code.

```ts
const validateCallback: (code: string) => Promise<Auth0UserAuth>;
```

##### Parameters

| name | type | description |
| ------ | -------- | ------------------------------------ |
| `code` | `string` | The authorization code from callback |

##### Returns

| type |
| ------------------------------------- |
| [`SpotifyUserAuth`](#spotifyuserauth) |

##### Errors

Request errors are thrown as [`OAuthRequestError`](/reference/oauth/interfaces#oauthrequesterror).

### `SpotifyUserAuth`

```ts
type SpotifyUserAuth = ProviderUserAuth & {
spotifyUser: Auth0User;
spotifyTokens: Auth0Tokens;
};
```

| type |
| ------------------------------------------------------------------ |
| [`ProviderUserAuth`](/reference/oauth/interfaces#provideruserauth) |
| [`SpotifyUser`](#spotifyuser) |
| [`SpotifyTokens`](#spotifytokens) |

```ts
import type { SpotifyTokens, SpotifyUser } from "@lucia-auth/oauth/providers";
```

### `SpotifyTokens`

```ts
type SpotifyTokens = {
accessToken: string;
refreshToken: string;
accessTokenExpiresIn: number;
scope: string;
tokenType: string;
};
```

### `SpotifyUser`

```ts
type SpotifyUser = {
country?: string;
display_name: string | null;
email?: string;
explicit_content: {
filter_enabled?: boolean;
filter_locked?: boolean;
};
external_urls: {
spotify: string;
};
followers: {
href: string | null;
total: number;
};
href: string;
id: string;
images: [
{
url: string;
height: number | null;
width: number | null;
}
];
product?: string;
type: string;
uri: string;
};
```
4 changes: 4 additions & 0 deletions documentation-v2/content/reference/oauth/providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ See [Patreon](/oauth/providers/patreon) provider.

See [Reddit](/oauth/providers/reddit) provider.

## `spotify()`

See [Spotify](/oauth/providers/spotify) provider.

## `twitch()`

See [Twitch](/oauth/providers/twitch) provider.
1 change: 1 addition & 0 deletions documentation-v2/src/env.d.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
/// <reference path="../.astro/types.d.ts" />
/// <reference types="astro/client" />
27 changes: 0 additions & 27 deletions packages/oauth/src/core.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { generateRandomString } from "lucia/utils";

import type { Auth, Key, LuciaError } from "lucia";
import type { CreateUserAttributesParameter, LuciaUser } from "./lucia.js";

Expand Down Expand Up @@ -34,31 +32,6 @@ export class OAuthRequestError extends Error {
}
}

export const generateState = () => {
return generateRandomString(43);
};

export const encodeBase64 = (s: string) => {
// ORDER IS IMPORTANT!!
// Buffer API EXISTS IN DENO!!
if (typeof window !== "undefined" && "Deno" in window) {
// deno
return btoa(s);
}
if (typeof Buffer === "function") {
// node
return Buffer.from(s).toString("base64");
}

// standard API
// IGNORE WARNING
return btoa(s);
};

export const scope = (base: string[], config: string[] = []) => {
return [...base, ...(config ?? [])].join(" ");
};

export const providerUserAuth = async <_Auth extends Auth>(
auth: _Auth,
providerId: string,
Expand Down
3 changes: 2 additions & 1 deletion packages/oauth/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { generateState, OAuthRequestError, providerUserAuth } from "./core.js";
export { OAuthRequestError, providerUserAuth } from "./core.js";
export { generateState } from "./utils.js";

export type { OAuthProvider } from "./core.js";
3 changes: 2 additions & 1 deletion packages/oauth/src/providers/auth0.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createUrl, handleRequest, authorizationHeaders } from "../request.js";
import { scope, generateState, providerUserAuth } from "../core.js";
import { providerUserAuth } from "../core.js";
import { scope, generateState } from "../utils.js";

import type { Auth } from "lucia";
import type { OAuthConfig, OAuthProvider } from "../core.js";
Expand Down
3 changes: 2 additions & 1 deletion packages/oauth/src/providers/discord.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createUrl, handleRequest, authorizationHeaders } from "../request.js";
import { providerUserAuth, generateState, scope } from "../core.js";
import { providerUserAuth } from "../core.js";
import { scope, generateState } from "../utils.js";

import type { Auth } from "lucia";
import type { OAuthConfig, OAuthProvider } from "../core.js";
Expand Down
3 changes: 2 additions & 1 deletion packages/oauth/src/providers/facebook.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createUrl, handleRequest, authorizationHeaders } from "../request.js";
import { scope, generateState, providerUserAuth } from "../core.js";
import { providerUserAuth } from "../core.js";
import { scope, generateState } from "../utils.js";

import type { Auth } from "lucia";
import type { OAuthConfig, OAuthProvider } from "../core.js";
Expand Down
3 changes: 2 additions & 1 deletion packages/oauth/src/providers/github.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createUrl, handleRequest, authorizationHeaders } from "../request.js";
import { scope, generateState, providerUserAuth } from "../core.js";
import { providerUserAuth } from "../core.js";
import { scope, generateState } from "../utils.js";

import type { Auth } from "lucia";
import type { OAuthConfig, OAuthProvider } from "../core.js";
Expand Down
3 changes: 2 additions & 1 deletion packages/oauth/src/providers/google.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createUrl, handleRequest, authorizationHeaders } from "../request.js";
import { scope, generateState, providerUserAuth } from "../core.js";
import { providerUserAuth } from "../core.js";
import { scope, generateState } from "../utils.js";

import type { Auth } from "lucia";
import type { OAuthConfig, OAuthProvider } from "../core.js";
Expand Down
1 change: 1 addition & 0 deletions packages/oauth/src/providers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ export { lichess, type LichessUser } from "./lichess.js";
export { linkedin, type LinkedinUser } from "./linkedin.js";
export { auth0, type Auth0User } from "./auth0.js";
export { facebook, type FacebookUser } from "./facebook.js";
export { spotify, type SpotifyUser } from "./spotify.js";
8 changes: 2 additions & 6 deletions packages/oauth/src/providers/lichess.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import { createUrl, handleRequest, authorizationHeaders } from "../request.js";
import {
scope,
generateState,
providerUserAuth,
encodeBase64
} from "../core.js";
import { providerUserAuth } from "../core.js";
import { scope, generateState, encodeBase64 } from "../utils.js";
import { generateRandomString } from "lucia/utils";

import type { Auth } from "lucia";
Expand Down
3 changes: 2 additions & 1 deletion packages/oauth/src/providers/linkedin.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createUrl, handleRequest, authorizationHeaders } from "../request.js";
import { scope, generateState, providerUserAuth } from "../core.js";
import { providerUserAuth } from "../core.js";
import { scope, generateState } from "../utils.js";

import type { Auth } from "lucia";
import type { OAuthConfig, OAuthProvider } from "../core.js";
Expand Down
3 changes: 2 additions & 1 deletion packages/oauth/src/providers/patreon.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createUrl, handleRequest, authorizationHeaders } from "../request.js";
import { scope, generateState, providerUserAuth } from "../core.js";
import { providerUserAuth } from "../core.js";
import { scope, generateState } from "../utils.js";

import type { Auth } from "lucia";
import type { OAuthConfig, OAuthProvider } from "../core.js";
Expand Down
8 changes: 2 additions & 6 deletions packages/oauth/src/providers/reddit.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import { createUrl, handleRequest, authorizationHeaders } from "../request.js";
import {
scope,
generateState,
providerUserAuth,
encodeBase64
} from "../core.js";
import { providerUserAuth } from "../core.js";
import { scope, generateState, encodeBase64 } from "../utils.js";

import type { Auth } from "lucia";
import type { OAuthConfig, OAuthProvider } from "../core.js";
Expand Down
Loading

0 comments on commit fb796bc

Please sign in to comment.