import express = require("express"); import * as passport from "passport"; import oauth2 = require("passport-oauth2"); export type OAuth2StrategyOptionsWithoutRequiredURLs = Pick< oauth2._StrategyOptionsBase, Exclude >; export interface _StrategyOptionsBase extends OAuth2StrategyOptionsWithoutRequiredURLs { authorizationURL?: string | undefined; callbackURL?: string | undefined; clientID: string; clientSecret: string; /** * Scopes (permissions) you might need to request to access Google APIs, * depending on the level of access you need. Sensitive scopes require * review by Google and have a _sensitive_ indicator on the Google Cloud * Platform (GCP) Console OAuth consent screen configuration page. * * One or many of these [Google * Scopes](https://developers.google.com/identity/protocols/oauth2/scopes) * * For a basic auth scenario, this is typically `['email', 'profile']` which * is an accepted abbreviation for `['https://www.googleapis.com/auth/userinfo.email', * 'https://www.googleapis.com/auth/userinfo.profile']`. */ scope?: string | string[] | undefined; tokenURL?: string | undefined; userProfileURL?: string | undefined; } export interface StrategyOptions extends _StrategyOptionsBase { passReqToCallback?: false | undefined; } export interface StrategyOptionsWithRequest extends _StrategyOptionsBase { passReqToCallback: true; } export interface Profile extends passport.Profile { profileUrl: string; provider: "google"; /** * An identifier for the user, unique among all Google accounts and * never reused. A Google account can have multiple email addresses at * different points in time, but the sub value is never changed. Use sub * within your application as the unique-identifier key for the user. * Maximum length of 255 case-sensitive ASCII characters. * * Ex: `"10769150350006150715113082367"` */ id: string; emails?: Array<{ value: string; verified: boolean }>; _raw: string; /** * ID Token payload, adhering to Google's implementation of the OpenID * Connect standard See * [documentation](https://developers.google.com/identity/protocols/oauth2/openid-connect#an-id-tokens-payload) * * An ID token is a JSON object containing a set of name/value pairs. Here's an example, formatted for readability: * ```json * { * "iss": "https://accounts.google.com", * "azp": "1234987819200.apps.googleusercontent.com", * "aud": "1234987819200.apps.googleusercontent.com", * "sub": "10769150350006150715113082367", * "at_hash": "HK6E_P6Dh8Y93mRNtsDB1Q", * "hd": "example.com", * "email": "jsmith@example.com", * "email_verified": true, * "iat": 1353601026, * "exp": 1353604926, * "nonce": "0394852-3190485-2490358" * } * ``` */ _json: { /** * The Issuer Identifier for the Issuer of the response. Always * https://accounts.google.com or accounts.google.com for Google ID * tokens. * * Ex: `"https://accounts.google.com"` */ iss: string; /** * The client_id of the authorized presenter. This claim is only needed when the * party requesting the ID token is not the same as the audience of the ID * token. This may be the case at Google for hybrid apps where a web application * and Android app have a different OAuth 2.0 client_id but share the same * Google APIs project. * * Ex: `"1234987819200.apps.googleusercontent.com"` */ azp?: string; /** * The audience that this ID token is intended for. It must be one of * the OAuth 2.0 client IDs of your application. * * Ex: `"1234987819200.apps.googleusercontent.com"` */ aud: string; /** * An identifier for the user, unique among all Google accounts and * never reused. A Google account can have multiple email addresses at * different points in time, but the sub value is never changed. Use sub * within your application as the unique-identifier key for the user. * Maximum length of 255 case-sensitive ASCII characters. * * Ex: `"10769150350006150715113082367"` */ sub: string; /** * Access token hash. Provides validation that the access token is tied to the * identity token. If the ID token is issued with an access_token value in the * server flow, this claim is always included. This claim can be used as an * alternate mechanism to protect against cross-site request forgery attacks, * but if you follow Step 1 and Step 3 it is not necessary to verify the access * token. * * Ex: `"HK6E_P6Dh8Y93mRNtsDB1Q"` */ at_hash?: string; /** * The time the ID token was issued. Represented in Unix time (integer * seconds). * * Ex: `1353601026` */ iat: number; /** * Expiration time on or after which the ID token must not be accepted. * Represented in Unix time (integer seconds). * * Ex: `1353604926` */ exp: number; /** * The user's email address. This value may not be unique to this user and is * not suitable for use as a primary key. Provided only if your scope included * the email scope value. * * Ex: `"jsmith@example.com"` */ email?: string; /** * True if the user's e-mail address has been verified; otherwise false. */ email_verified?: boolean; /** * The user's given name(s) or first name(s). Might be provided when a name * claim is present. */ given_name?: string; /** * The user's surname(s) or last name(s). Might be provided when a name claim is * present. */ family_name?: string; /** * The user's full name, in a displayable form. Might be provided when: * * - The request scope included the string "profile" * - The ID token is returned from a token refresh * * When name claims are present, you can use them to update your app's user records. Note that this claim is never guaranteed to be present. */ name?: string; /** * The hosted G Suite domain of the user. Provided only if the user belongs to a * hosted domain. * * Ex: `"example.com"` */ hd?: string; /** * The user's locale, represented by a [BCP 47](https://www.rfc-editor.org/info/bcp47) language tag. Might be provided * when a name claim is present. * * Ex: `"en"` */ locale?: string; /** * The value of the nonce supplied by your app in the authentication * request. You should enforce protection against replay attacks by ensuring * it is presented only once. * * Ex: `"0394852-3190485-2490358"` */ nonce?: string; /** * The URL of the user's profile picture. Might be provided when: * - The request scope included the string "profile" * - The ID token is returned from a token refresh When picture claims * are present, you can use them to update your app's user records. * Note that this claim is never guaranteed to be present. * * Ex: `"https://lh4.googleusercontent.com/-aBcDeFG/ABCDEFGHI/JSKLMNO/pQRstuv/s99-c/photo.jpg"` */ picture?: string; /** * The URL of the user's profile page. Might be provided when: * - The request scope included the string "profile" * - The ID token is returned from a token refresh When profile claims * are present, you can use them to update your app's user records. * Note that this claim is never guaranteed to be present. */ profile?: string; }; } export type VerifyCallback = oauth2.VerifyCallback; export class Strategy extends oauth2.Strategy { constructor( options: StrategyOptions, verify: (accessToken: string, refreshToken: string, profile: Profile, done: VerifyCallback) => void, ); constructor( options: StrategyOptions, verify: ( accessToken: string, refreshToken: string, params: GoogleCallbackParameters, profile: Profile, done: VerifyCallback, ) => void, ); constructor( options: StrategyOptionsWithRequest, verify: ( req: express.Request, accessToken: string, refreshToken: string, profile: Profile, done: VerifyCallback, ) => void, ); constructor( options: StrategyOptionsWithRequest, verify: ( req: express.Request, accessToken: string, refreshToken: string, params: GoogleCallbackParameters, profile: Profile, done: VerifyCallback, ) => void, ); } // additional Google-specific options export interface AuthenticateOptionsGoogle extends passport.AuthenticateOptions { accessType?: "offline" | "online" | undefined; prompt?: string | undefined; loginHint?: string | undefined; includeGrantedScopes?: boolean | undefined; display?: string | undefined; hostedDomain?: string | undefined; hd?: string | undefined; requestVisibleActions?: any; openIDRealm?: any; } export interface GoogleCallbackParameters { access_token: string; refresh_token?: string | undefined; id_token?: string | undefined; expires_in: number; scope: string; token_type: string; } // allow Google-specific options when using "google" strategy declare module "passport" { interface Authenticator< InitializeRet = express.Handler, AuthenticateRet = any, AuthorizeRet = AuthenticateRet, AuthorizeOptions = passport.AuthenticateOptions, > { authenticate( strategy: "google", options: AuthenticateOptionsGoogle, callback?: (...args: any[]) => any, ): AuthenticateRet; authorize( strategy: "google", options: AuthenticateOptionsGoogle, callback?: (...args: any[]) => any, ): AuthorizeRet; } }