From 9de89b2a2e4b65312e596c770db0ad210978646e Mon Sep 17 00:00:00 2001 From: Ewout Stortenbeker <4ewout@gmail.com> Date: Fri, 20 May 2022 14:44:20 +0200 Subject: [PATCH] updated OAuthProvider types --- src/oauth-providers/dropbox.ts | 9 +++++---- src/oauth-providers/facebook.ts | 9 +++++---- src/oauth-providers/google.ts | 9 +++++---- src/oauth-providers/index.ts | 3 ++- src/oauth-providers/instagram.ts | 9 +++++---- src/oauth-providers/oauth-provider.ts | 18 ++++++++++++++---- src/oauth-providers/spotify.ts | 9 +++++---- src/shared/env.ts | 4 ++-- 8 files changed, 43 insertions(+), 27 deletions(-) diff --git a/src/oauth-providers/dropbox.ts b/src/oauth-providers/dropbox.ts index 949faa1..d35731e 100644 --- a/src/oauth-providers/dropbox.ts +++ b/src/oauth-providers/dropbox.ts @@ -1,4 +1,4 @@ -import { IOAuth2Provider, IOAuth2ProviderSettings, IOAuth2AuthCodeParams, IOAuth2RefreshTokenParams } from "./oauth-provider"; +import { IOAuth2ProviderSettings, IOAuth2AuthCodeParams, IOAuth2RefreshTokenParams, OAuth2Provider, OAuth2ProviderInitInfo } from "./oauth-provider"; import { fetch } from '../shared/simple-fetch'; /** @@ -48,9 +48,10 @@ interface IDropboxUser { } team_member_id?: string } -export class DropboxAuthProvider implements IOAuth2Provider { +export class DropboxAuthProvider extends OAuth2Provider { - constructor(private settings: IDropboxAuthSettings) { + constructor(settings: IDropboxAuthSettings) { + super(settings); if (!settings.scopes) { settings.scopes = []; } if (settings.scopes.length > 0 && !settings.scopes.includes('account_info.read')) { settings.scopes.push('account_info.read'); } } @@ -66,7 +67,7 @@ export class DropboxAuthProvider implements IOAuth2Provider { * @param info.options.locale If the locale specified is a supported language, Dropbox will direct users to a translated version of the authorization website. Locale tags should be IETF language tags. * @param info.options.force_reauthentication When true (default is false) users will be signed out if they are currently signed in. This will make sure the user is brought to a page where they can create a new account or sign in to another account. This should only be used when there is a definite reason to believe that the user needs to sign in to a new or different account. */ - async init(info: { redirect_url: string, state?: string, options?: { locale?: string, require_role?: string, disable_signup?: boolean, force_reapprove?: boolean, force_reauthentication?: boolean } }) { + async init(info: OAuth2ProviderInitInfo) { // Return url to get authorization code with const options = info.options || {}; const authUrl = `https://www.dropbox.com/oauth2/authorize?response_type=code&token_access_type=offline&client_id=${this.settings.client_id}&scope=${encodeURIComponent(this.settings.scopes.join(' '))}&redirect_uri=${encodeURIComponent(info.redirect_url)}&locale=${options.locale || ''}&require_role=${options.require_role || ''}&force_reauthentication=${options.force_reauthentication === true}&force_reapprove=${options.force_reapprove === true}&disable_signup=${options.disable_signup === true}&state=${encodeURIComponent(info.state)}`; diff --git a/src/oauth-providers/facebook.ts b/src/oauth-providers/facebook.ts index a31b1e8..246d4b9 100644 --- a/src/oauth-providers/facebook.ts +++ b/src/oauth-providers/facebook.ts @@ -1,4 +1,4 @@ -import { IOAuth2Provider, IOAuth2ProviderSettings, IOAuth2AuthCodeParams, IOAuth2RefreshTokenParams } from "./oauth-provider"; +import { IOAuth2ProviderSettings, IOAuth2AuthCodeParams, IOAuth2RefreshTokenParams, OAuth2Provider, OAuth2ProviderInitInfo } from "./oauth-provider"; import { fetch } from '../shared/simple-fetch'; /** @@ -28,9 +28,10 @@ interface IFacebookUser { } } } -export class FacebookAuthProvider implements IOAuth2Provider { +export class FacebookAuthProvider extends OAuth2Provider { - constructor(private settings: IFacebookAuthSettings) { + constructor(settings: IFacebookAuthSettings) { + super(settings); if (!settings.scopes) { settings.scopes = []; } if (!settings.scopes.includes('email')) { settings.scopes.push('email'); } } @@ -40,7 +41,7 @@ export class FacebookAuthProvider implements IOAuth2Provider { * @param info.redirectUrl Url spotify will redirect to after authorizing, should be the url * @param info.state Optional state that will be passed to redirectUri by spotify */ - async init(info: { redirect_url: string, state?: string }) { + async init(info: OAuth2ProviderInitInfo) { // Return url to get authorization code with const authUrl = `https://www.facebook.com/v7.0/dialog/oauth?response_type=code&client_id=${this.settings.client_id}&scope=${encodeURIComponent(this.settings.scopes.join(' '))}&redirect_uri=${encodeURIComponent(info.redirect_url)}&state=${encodeURIComponent(info.state)}`; return authUrl; diff --git a/src/oauth-providers/google.ts b/src/oauth-providers/google.ts index 5afe2a8..e7970aa 100644 --- a/src/oauth-providers/google.ts +++ b/src/oauth-providers/google.ts @@ -1,4 +1,4 @@ -import { IOAuth2Provider, IOAuth2ProviderSettings, IOpenIDToken, IOpenIDConfiguration, IOAuth2AuthCodeParams, IOAuth2RefreshTokenParams } from "./oauth-provider"; +import { IOAuth2ProviderSettings, IOpenIDToken, IOpenIDConfiguration, IOAuth2AuthCodeParams, IOAuth2RefreshTokenParams, OAuth2ProviderInitInfo, OAuth2Provider } from "./oauth-provider"; import { fetch } from '../shared/simple-fetch'; /** @@ -43,11 +43,12 @@ interface IGoogleUser { updated_at: number } -export class GoogleAuthProvider implements IOAuth2Provider { +export class GoogleAuthProvider extends OAuth2Provider { _config: IOpenIDConfiguration - constructor(private settings: IGoogleAuthSettings) { + constructor(settings: IGoogleAuthSettings) { + super(settings); if (!settings.scopes) { settings.scopes = []; } if (!settings.scopes.includes('email')) { settings.scopes.push('email'); } if (!settings.scopes.includes('profile')) { settings.scopes.push('profile'); } @@ -66,7 +67,7 @@ export class GoogleAuthProvider implements IOAuth2Provider { * @param info.redirectUrl Url spotify will redirect to after authorizing, should be the url * @param info.state Optional state that will be passed to redirectUri by spotify */ - async init(info: { redirect_url: string, state?: string }) { + async init(info: OAuth2ProviderInitInfo) { // Return url to get authorization code with // See https://developers.google.com/identity/protocols/oauth2/web-server#httprest diff --git a/src/oauth-providers/index.ts b/src/oauth-providers/index.ts index dffabaa..ffb9528 100644 --- a/src/oauth-providers/index.ts +++ b/src/oauth-providers/index.ts @@ -2,6 +2,7 @@ import { DropboxAuthProvider } from './dropbox'; import { FacebookAuthProvider } from './facebook'; import { GoogleAuthProvider } from './google'; import { InstagramAuthProvider } from './instagram'; +import { OAuth2Provider } from './oauth-provider'; import { SpotifyAuthProvider } from './spotify'; export { DropboxAuthProvider, IDropboxAuthSettings } from './dropbox'; @@ -10,7 +11,7 @@ export { GoogleAuthProvider, IGoogleAuthSettings } from './google'; export { InstagramAuthProvider, IInstagramAuthSettings } from './instagram'; export { SpotifyAuthProvider, ISpotifyAuthSettings } from './spotify'; -const oAuth2Providers = { +const oAuth2Providers: { [key: string]: typeof OAuth2Provider } = { dropbox: DropboxAuthProvider, facebook: FacebookAuthProvider, google: GoogleAuthProvider, diff --git a/src/oauth-providers/instagram.ts b/src/oauth-providers/instagram.ts index ad6691c..9337b3f 100644 --- a/src/oauth-providers/instagram.ts +++ b/src/oauth-providers/instagram.ts @@ -1,4 +1,4 @@ -import { IOAuth2Provider, IOAuth2ProviderSettings, IOAuth2AuthCodeParams } from "./oauth-provider"; +import { IOAuth2ProviderSettings, IOAuth2AuthCodeParams, OAuth2Provider, OAuth2ProviderInitInfo } from "./oauth-provider"; import { fetch } from '../shared/simple-fetch'; /** @@ -28,9 +28,10 @@ interface IInstagramUser { followed_by: number } } -export class InstagramAuthProvider implements IOAuth2Provider { +export class InstagramAuthProvider extends OAuth2Provider { - constructor(private settings: IInstagramAuthSettings) { + constructor(settings: IInstagramAuthSettings) { + super(settings); if (!settings.scopes) { settings.scopes = []; } if (!settings.scopes.includes('basic')) { settings.scopes.push('basic'); } @@ -44,7 +45,7 @@ export class InstagramAuthProvider implements IOAuth2Provider { * @param info.redirectUrl Url spotify will redirect to after authorizing, should be the url * @param info.state Optional state that will be passed to redirectUri by spotify */ - async init(info: { redirect_url: string, state?: string }) { + async init(info: OAuth2ProviderInitInfo) { // Return url to get authorization code with // See https://www.instagram.com/developer/authorization/ const authUrl = `https://api.instagram.com/oauth/authorize/?response_type=code&client_id=${this.settings.client_id}&scope=${encodeURIComponent(this.settings.scopes.join(' '))}&redirect_uri=${encodeURIComponent(info.redirect_url)}&state=${encodeURIComponent(info.state)}`; diff --git a/src/oauth-providers/oauth-provider.ts b/src/oauth-providers/oauth-provider.ts index 9c5fc87..e453bbc 100644 --- a/src/oauth-providers/oauth-provider.ts +++ b/src/oauth-providers/oauth-provider.ts @@ -18,10 +18,20 @@ export interface IOAuth2TokenResult { expires_in?: number, refresh_token?: string } -export interface IOAuth2Provider { - init(info: { redirect_url: string, state?: string, options?: any }): Promise - getAccessToken(params: IOAuth2AuthCodeParams | IOAuth2RefreshTokenParams): Promise - getUserInfo(access_token: string): Promise<{ id: string, name: string, display_name: string, picture?: Array<{ width?: number, height?: number, url: string }>, email: string, email_verified: boolean, other?: { [key:string]: string|number|boolean } }> + +export type OAuth2ProviderInitInfo = { redirect_url: string, state?: string, options?: any }; +export type OAuth2ProviderUserInfo = { id: string, name: string, display_name: string, picture?: Array<{ width?: number, height?: number, url: string }>, email: string, email_verified: boolean, other?: { [key:string]: string|number|boolean } }; +// export interface IOAuth2Provider { +// init(info: OAuth2ProviderInitInfo): Promise; +// getAccessToken(param: OAuth2ProviderGetAccessTokenParams): Promise; +// getUserInfo(access_token: string): Promise; +// } +class NotImplementedError extends Error { constructor() { super('Not implemented'); } } +export class OAuth2Provider { //implements IOAuth2Provider + constructor(protected settings: IOAuth2ProviderSettings) {} + init(info: OAuth2ProviderInitInfo): Promise { throw new NotImplementedError(); }; + getAccessToken(param: IOAuth2AuthCodeParams|IOAuth2RefreshTokenParams): Promise { throw new NotImplementedError(); };; + getUserInfo(access_token: string): Promise { throw new NotImplementedError(); };; } // For OAuth2.0 providers with id_token in getAccessToken, see https://auth0.com/docs/tokens/concepts/jwts export interface IOpenIDToken { diff --git a/src/oauth-providers/spotify.ts b/src/oauth-providers/spotify.ts index dc4b187..82e1913 100644 --- a/src/oauth-providers/spotify.ts +++ b/src/oauth-providers/spotify.ts @@ -1,4 +1,4 @@ -import { IOAuth2Provider, IOAuth2ProviderSettings, IOAuth2AuthCodeParams, IOAuth2RefreshTokenParams } from "./oauth-provider"; +import { IOAuth2ProviderSettings, IOAuth2AuthCodeParams, IOAuth2RefreshTokenParams, OAuth2Provider, OAuth2ProviderInitInfo } from "./oauth-provider"; import { fetch } from '../shared/simple-fetch'; /** @@ -28,9 +28,10 @@ interface ISpotifyUser { type: 'user', uri: string } -export class SpotifyAuthProvider implements IOAuth2Provider { +export class SpotifyAuthProvider extends OAuth2Provider { - constructor(private settings: ISpotifyAuthSettings) { + constructor(settings: ISpotifyAuthSettings) { + super(settings); if (!settings.scopes) { settings.scopes = []; } if (!settings.scopes.includes('user-read-email')) { settings.scopes.push('user-read-email'); } if (!settings.scopes.includes('user-read-private')) { settings.scopes.push('user-read-private'); } @@ -41,7 +42,7 @@ export class SpotifyAuthProvider implements IOAuth2Provider { * @param info.redirectUrl Url spotify will redirect to after authorizing * @param info.state Optional state that will be passed to redirectUri by spotify */ - async init(info: { redirect_url: string, state?: string }) { + async init(info: OAuth2ProviderInitInfo) { // Return url to get authorization code with const authUrl = `https://accounts.spotify.com/authorize?response_type=code&client_id=${this.settings.client_id}&scope=${encodeURIComponent(this.settings.scopes.join(' '))}&redirect_uri=${encodeURIComponent(info.redirect_url)}&state=${encodeURIComponent(info.state)}`; return authUrl; diff --git a/src/shared/env.ts b/src/shared/env.ts index 203356e..6bd862f 100644 --- a/src/shared/env.ts +++ b/src/shared/env.ts @@ -4,7 +4,7 @@ import type { AceBaseServerConfig } from '../settings'; import type { DbUserAccountDetails } from '../schema/user'; import type { ConnectedClient } from './clients'; import type { DebugLogger, SimpleCache } from 'acebase-core'; -import type { IOAuth2Provider } from '../oauth-providers/oauth-provider'; +import type { OAuth2Provider } from '../oauth-providers/oauth-provider'; import type { Server as HttpServer } from 'http'; import type { Server as SecureHttpServer } from 'https'; import type { PathBasedRules } from '../rules'; @@ -23,7 +23,7 @@ export interface RouteInitEnvironment { tokenSalt: string; clients: Map; authCache: SimpleCache; - authProviders: { [providerName: string]: IOAuth2Provider }; + authProviders: { [providerName: string]: OAuth2Provider }; rules: PathBasedRules };