Skip to content

Commit

Permalink
Add Dropbox OAuth provider (#1014)
Browse files Browse the repository at this point in the history
  • Loading branch information
pilcrowonpaper authored Aug 22, 2023
1 parent 2e28b4d commit b54fca3
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 1 deletion.
6 changes: 6 additions & 0 deletions .auri/$ncghazw5.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
package: "@lucia-auth/minor" # package name
type: "minor" # "major", "minor", "patch"
---

Add Dropbox provider
152 changes: 152 additions & 0 deletions packages/oauth/src/providers/dropbox.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import {
OAuth2ProviderAuth,
createOAuth2AuthorizationUrl,
validateOAuth2AuthorizationCode
} from "../core/oauth2.js";
import { ProviderUserAuth } from "../core/provider.js";
import { handleRequest, authorizationHeader } from "../utils/request.js";

import type { Auth } from "lucia";

type Config = {
clientId: string;
clientSecret: string;
redirectUri: string;
scope?: string[];
};

const PROVIDER_ID = "dropbox";

export const dropbox = <_Auth extends Auth = Auth>(
auth: _Auth,
config: Config
): DropboxAuth<_Auth> => {
return new DropboxAuth(auth, config);
};

export class DropboxAuth<_Auth extends Auth = Auth> extends OAuth2ProviderAuth<
DropboxUserAuth<_Auth>
> {
private config: Config;

constructor(auth: _Auth, config: Config) {
super(auth);

this.config = config;
}

public getAuthorizationUrl = async (): Promise<
readonly [url: URL, state: string]
> => {
const scopeConfig = this.config.scope ?? [];
return await createOAuth2AuthorizationUrl(
"https://www.dropbox.com/oauth2/authorize",
{
clientId: this.config.clientId,
redirectUri: this.config.redirectUri,
scope: ["account_info.read", ...scopeConfig]
}
);
};

public validateCallback = async (
code: string
): Promise<DropboxUserAuth<_Auth>> => {
const dropboxTokens = await this.validateAuthorizationCode(code);
const dropboxUser = await getDropboxUser(dropboxTokens.accessToken);
return new DropboxUserAuth(this.auth, dropboxUser, dropboxTokens);
};

private validateAuthorizationCode = async (
code: string
): Promise<DropboxTokens> => {
const tokens = await validateOAuth2AuthorizationCode<{
access_token: string;
expires_in: number;
refresh_token?: string;
}>(code, "https://www.dropbox.com/oauth2/token", {
clientId: this.config.clientId,
redirectUri: this.config.redirectUri,
clientPassword: {
authenticateWith: "client_secret",
clientSecret: this.config.clientSecret
}
});
return {
accessToken: tokens.access_token,
accessTokenExpiresIn: tokens.expires_in,
refreshToken: tokens.refresh_token ?? null
};
};
}

export class DropboxUserAuth<
_Auth extends Auth = Auth
> extends ProviderUserAuth<_Auth> {
public dropboxTokens: DropboxTokens;
public dropboxUser: DropboxUser;

constructor(
auth: _Auth,
dropboxUser: DropboxUser,
dropboxTokens: DropboxTokens
) {
super(auth, PROVIDER_ID, dropboxUser.account_id);

this.dropboxTokens = dropboxTokens;
this.dropboxUser = dropboxUser;
}
}

const getDropboxUser = async (accessToken: string): Promise<DropboxUser> => {
const request = new Request(
"https://api.dropboxapi.com/2/users/get_current_account",
{
headers: {
Authorization: authorizationHeader("bearer", accessToken)
}
}
);
const dropboxUser = await handleRequest<DropboxUser>(request);
return dropboxUser;
};

export type DropboxTokens = {
accessToken: string;
accessTokenExpiresIn: number;
refreshToken: string | null;
};

export type DropboxUser = PairedDropBoxUser | UnpairedDropboxUser;

type PairedDropBoxUser = BaseDropboxUser & {
is_paired: true;
team: {
id: string;
name: string;
office_addin_policy: Record<string, string>;
sharing_policies: Record<string, Record<string, string>>;
};
};

type UnpairedDropboxUser = BaseDropboxUser & {
is_paired: false;
};

type BaseDropboxUser = {
account_id: string;
country: string;
disabled: boolean;
email: string;
email_verified: boolean;

locale: string;
name: {
abbreviated_name: string;
display_name: string;
familiar_name: string;
given_name: string;
surname: string;
};
profile_photo_url: string;
};
2 changes: 1 addition & 1 deletion packages/oauth/src/providers/gitlab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ export class GitlabUserAuth<
}

const getGitlabUser = async (accessToken: string): Promise<GitlabUser> => {
const request = new Request("https://api.gitlab.com/me", {
const request = new Request("https://gitlab.com/api/v4/user", {
headers: {
Authorization: authorizationHeader("bearer", accessToken)
}
Expand Down
8 changes: 8 additions & 0 deletions packages/oauth/src/providers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ export type {
DiscordUserAuth
} from "./discord.js";

export { dropbox } from "./dropbox.js";
export type {
DropboxAuth,
DropboxTokens,
DropboxUser,
DropboxUserAuth
} from "./dropbox.js";

export { facebook } from "./facebook.js";
export type {
FacebookAuth,
Expand Down

0 comments on commit b54fca3

Please sign in to comment.