Merge pull request #997 from spotify/google-refreh-tokens
Implemented refresh endpoint for google provider
This commit is contained in:
@@ -58,9 +58,13 @@ function createEnv(plugin: string): PluginEnvironment {
|
||||
|
||||
async function main() {
|
||||
const app = express();
|
||||
const corsOptions: cors.CorsOptions = {
|
||||
origin: 'http://localhost:3000',
|
||||
credentials: true,
|
||||
};
|
||||
|
||||
app.use(helmet());
|
||||
app.use(cors());
|
||||
app.use(cors(corsOptions));
|
||||
app.use(compression());
|
||||
app.use(express.json());
|
||||
app.use(requestLoggingHandler());
|
||||
|
||||
@@ -40,7 +40,7 @@ type CreateOptions = {
|
||||
export type GoogleAuthResponse = {
|
||||
accessToken: string;
|
||||
idToken: string;
|
||||
scopes: string;
|
||||
scope: string;
|
||||
expiresInSeconds: number;
|
||||
};
|
||||
|
||||
@@ -70,7 +70,7 @@ class GoogleAuth implements OAuthApi, OpenIdConnectApi {
|
||||
return {
|
||||
idToken: res.idToken,
|
||||
accessToken: res.accessToken,
|
||||
scopes: GoogleAuth.normalizeScopes(res.scopes),
|
||||
scopes: GoogleAuth.normalizeScopes(res.scope),
|
||||
expiresAt: new Date(Date.now() + res.expiresInSeconds * 1000),
|
||||
};
|
||||
},
|
||||
|
||||
+1
-1
@@ -99,7 +99,7 @@ export class DefaultAuthConnector<AuthSession>
|
||||
}
|
||||
|
||||
async refreshSession(): Promise<any> {
|
||||
const res = await fetch(this.buildUrl('/token', { optional: true }), {
|
||||
const res = await fetch(this.buildUrl('/refresh', { optional: true }), {
|
||||
headers: {
|
||||
'x-requested-with': 'XMLHttpRequest',
|
||||
},
|
||||
|
||||
@@ -25,10 +25,16 @@
|
||||
"morgan": "^1.10.0",
|
||||
"winston": "^3.2.1",
|
||||
"yn": "^4.0.0",
|
||||
"passport": "0.4.1",
|
||||
"passport-google-oauth20": "2.0.0",
|
||||
"@types/passport": "1.0.3",
|
||||
"@types/passport-google-oauth20": "2.0.3"
|
||||
"passport": "^0.4.1",
|
||||
"passport-google-oauth20": "^2.0.0",
|
||||
"passport-oauth2-refresh": "^2.0.0",
|
||||
"passport-oauth2": "^1.5.0",
|
||||
"cookie-parser": "^1.4.5",
|
||||
"@types/passport-oauth2-refresh": "^1.1.1",
|
||||
"@types/passport": "^1.0.3",
|
||||
"@types/passport-google-oauth20": "^2.0.3",
|
||||
"@types/cookie-parser": "^1.4.2",
|
||||
"@types/passport-oauth2": "^1.4.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.6",
|
||||
|
||||
@@ -14,10 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { GoogleAuthProvider } from './provider';
|
||||
import { GoogleAuthProvider, THOUSAND_DAYS_MS } from './provider';
|
||||
import passport from 'passport';
|
||||
import express from 'express';
|
||||
import * as utils from './../utils';
|
||||
import refresh from 'passport-oauth2-refresh';
|
||||
|
||||
const googleAuthProviderConfig = {
|
||||
provider: 'google',
|
||||
@@ -51,7 +52,7 @@ describe('GoogleAuthProvider', () => {
|
||||
});
|
||||
|
||||
describe('start authentication handler', () => {
|
||||
const mockResponse: any = ({} as unknown) as express.Response;
|
||||
const mockResponse = ({} as unknown) as express.Response;
|
||||
const mockNext: express.NextFunction = jest.fn();
|
||||
|
||||
it('should initiate authenticate request with provided scopes', () => {
|
||||
@@ -97,6 +98,7 @@ describe('GoogleAuthProvider', () => {
|
||||
it('should perform logout and respond with 200', () => {
|
||||
const mockResponse: any = ({
|
||||
send: jest.fn(),
|
||||
cookie: jest.fn(),
|
||||
} as unknown) as express.Response;
|
||||
|
||||
const googleAuthProvider = new GoogleAuthProvider(
|
||||
@@ -110,23 +112,68 @@ describe('GoogleAuthProvider', () => {
|
||||
googleAuthProvider.logout(mockRequest, mockResponse);
|
||||
expect(spyResponse).toBeCalledTimes(1);
|
||||
expect(spyResponse).toBeCalledWith('logout!');
|
||||
expect(mockResponse.cookie).toBeCalledTimes(1);
|
||||
expect(mockResponse.cookie).toBeCalledWith(
|
||||
'google-refresh-token',
|
||||
'',
|
||||
expect.objectContaining({ maxAge: 0 }),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('redirect frame handler', () => {
|
||||
const mockRequest = ({} as unknown) as express.Request;
|
||||
const mockResponse: any = ({
|
||||
send: jest.fn(),
|
||||
status: jest.fn().mockReturnThis(),
|
||||
send: jest.fn().mockReturnThis(),
|
||||
cookie: jest.fn().mockReturnThis(),
|
||||
} as unknown) as express.Response;
|
||||
const mockNext: express.NextFunction = jest.fn();
|
||||
|
||||
it('should call authenticate and post a response ', () => {
|
||||
it('should call authenticate and post a response', () => {
|
||||
const spyPostMessage = jest
|
||||
.spyOn(utils, 'postMessageResponse')
|
||||
.mockImplementation(() => jest.fn());
|
||||
|
||||
const spyPassport = jest
|
||||
.spyOn(passport, 'authenticate')
|
||||
.mockImplementation((_x, callbackFunc) => {
|
||||
const cb = callbackFunc as Function;
|
||||
cb(null, { refreshToken: 'REFRESH_TOKEN' });
|
||||
return jest.fn();
|
||||
});
|
||||
|
||||
const googleAuthProvider = new GoogleAuthProvider(
|
||||
googleAuthProviderConfig,
|
||||
);
|
||||
|
||||
googleAuthProvider.frameHandler(mockRequest, mockResponse, mockNext);
|
||||
expect(spyPassport).toBeCalledTimes(1);
|
||||
expect(spyPostMessage).toBeCalledTimes(1);
|
||||
expect(mockResponse.cookie).toBeCalledTimes(1);
|
||||
expect(mockResponse.cookie).toBeCalledWith(
|
||||
'google-refresh-token',
|
||||
'REFRESH_TOKEN',
|
||||
expect.objectContaining({
|
||||
path: '/auth/google',
|
||||
sameSite: 'none',
|
||||
httpOnly: true,
|
||||
maxAge: THOUSAND_DAYS_MS,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should respond with a error message if no refresh token returned', () => {
|
||||
const spyPassport = jest
|
||||
.spyOn(passport, 'authenticate')
|
||||
.mockImplementation((_x, callbackFunc) => {
|
||||
const cb = callbackFunc as Function;
|
||||
cb(null, {});
|
||||
return jest.fn();
|
||||
});
|
||||
|
||||
const spyPostMessage = jest
|
||||
.spyOn(utils, 'postMessageResponse')
|
||||
.mockImplementation(() => jest.fn());
|
||||
|
||||
const googleAuthProvider = new GoogleAuthProvider(
|
||||
@@ -134,10 +181,38 @@ describe('GoogleAuthProvider', () => {
|
||||
);
|
||||
|
||||
googleAuthProvider.frameHandler(mockRequest, mockResponse, mockNext);
|
||||
const callbackFunc = spyPassport.mock.calls[0][1] as Function;
|
||||
callbackFunc();
|
||||
expect(spyPassport).toBeCalledTimes(1);
|
||||
expect(spyPostMessage).toBeCalledTimes(1);
|
||||
expect(spyPostMessage).toBeCalledWith(mockResponse, {
|
||||
type: 'auth-result',
|
||||
error: new Error('Missing refresh token'),
|
||||
});
|
||||
});
|
||||
|
||||
it('should respond with a error message if auth failed', () => {
|
||||
const spyPassport = jest
|
||||
.spyOn(passport, 'authenticate')
|
||||
.mockImplementation((_x, callbackFunc) => {
|
||||
const cb = callbackFunc as Function;
|
||||
cb(new Error('TokenError'), null);
|
||||
return jest.fn();
|
||||
});
|
||||
|
||||
const spyPostMessage = jest
|
||||
.spyOn(utils, 'postMessageResponse')
|
||||
.mockImplementation(() => jest.fn());
|
||||
|
||||
const googleAuthProvider = new GoogleAuthProvider(
|
||||
googleAuthProviderConfig,
|
||||
);
|
||||
|
||||
googleAuthProvider.frameHandler(mockRequest, mockResponse, mockNext);
|
||||
expect(spyPassport).toBeCalledTimes(1);
|
||||
expect(spyPostMessage).toBeCalledTimes(1);
|
||||
expect(spyPostMessage).toBeCalledWith(mockResponse, {
|
||||
type: 'auth-result',
|
||||
error: new Error('Google auth failed, Error: TokenError'),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -159,4 +234,159 @@ describe('GoogleAuthProvider', () => {
|
||||
}).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('refresh token handler', () => {
|
||||
const mockResponse = ({
|
||||
status: jest.fn().mockReturnThis(),
|
||||
send: jest.fn().mockReturnThis(),
|
||||
} as unknown) as express.Response;
|
||||
|
||||
describe('no refresh token cookie', () => {
|
||||
it('should respond with a 401', () => {
|
||||
const mockRequest = ({
|
||||
cookies: jest.fn(),
|
||||
} as unknown) as express.Request;
|
||||
|
||||
const googleAuthProvider = new GoogleAuthProvider(
|
||||
googleAuthProviderConfig,
|
||||
);
|
||||
|
||||
googleAuthProvider.refresh(mockRequest, mockResponse);
|
||||
expect(mockResponse.send).toBeCalledTimes(1);
|
||||
expect(mockResponse.send).toBeCalledWith('Missing session cookie');
|
||||
|
||||
expect(mockResponse.status).toBeCalledTimes(1);
|
||||
expect(mockResponse.status).toBeCalledWith(401);
|
||||
});
|
||||
});
|
||||
|
||||
describe('refresh token cookie, no scope', () => {
|
||||
const mockRequest = ({
|
||||
cookies: { 'google-refresh-token': 'REFRESH_TOKEN' },
|
||||
query: {},
|
||||
} as unknown) as express.Request;
|
||||
|
||||
const googleAuthProvider = new GoogleAuthProvider(
|
||||
googleAuthProviderConfig,
|
||||
);
|
||||
|
||||
it('should request for a new access token and fail if no access token returned', () => {
|
||||
const spyRefresh = jest
|
||||
.spyOn(refresh, 'requestNewAccessToken')
|
||||
.mockImplementation((_x, _y, _z, callbackFunc) => {
|
||||
const cb = callbackFunc as Function;
|
||||
cb(undefined, undefined, undefined, {});
|
||||
});
|
||||
|
||||
googleAuthProvider.refresh(mockRequest, mockResponse);
|
||||
expect(spyRefresh).toBeCalledTimes(1);
|
||||
expect(spyRefresh).toBeCalledWith(
|
||||
'google',
|
||||
'REFRESH_TOKEN',
|
||||
{},
|
||||
expect.any(Function),
|
||||
);
|
||||
expect(mockResponse.status).toBeCalledTimes(1);
|
||||
expect(mockResponse.status).toBeCalledWith(401);
|
||||
expect(mockResponse.send).toBeCalledTimes(1);
|
||||
expect(mockResponse.send).toBeCalledWith(
|
||||
'Failed to refresh access token',
|
||||
);
|
||||
});
|
||||
|
||||
it('should request for a new access token and return 401 if any error', () => {
|
||||
const spyRefresh = jest
|
||||
.spyOn(refresh, 'requestNewAccessToken')
|
||||
.mockImplementation((_x, _y, _z, callbackFunc) => {
|
||||
const cb = callbackFunc as Function;
|
||||
cb({ error: 'ERROR' }, undefined, undefined, {});
|
||||
});
|
||||
|
||||
googleAuthProvider.refresh(mockRequest, mockResponse);
|
||||
expect(spyRefresh).toBeCalledTimes(1);
|
||||
expect(spyRefresh).toBeCalledWith(
|
||||
'google',
|
||||
'REFRESH_TOKEN',
|
||||
{},
|
||||
expect.any(Function),
|
||||
);
|
||||
expect(mockResponse.status).toBeCalledTimes(1);
|
||||
expect(mockResponse.status).toBeCalledWith(401);
|
||||
expect(mockResponse.send).toBeCalledTimes(1);
|
||||
expect(mockResponse.send).toBeCalledWith(
|
||||
'Failed to refresh access token',
|
||||
);
|
||||
});
|
||||
|
||||
it('should fetch and return a new access token', () => {
|
||||
const spyRefresh = jest
|
||||
.spyOn(refresh, 'requestNewAccessToken')
|
||||
.mockImplementation((_x, _y, _z, callbackFunc) => {
|
||||
const cb = callbackFunc as Function;
|
||||
cb(undefined, 'ACCESS_TOKEN', undefined, {
|
||||
expires_in: 'EXPIRES_IN',
|
||||
id_token: 'ID_TOKEN',
|
||||
});
|
||||
});
|
||||
|
||||
googleAuthProvider.refresh(mockRequest, mockResponse);
|
||||
expect(spyRefresh).toBeCalledTimes(1);
|
||||
expect(spyRefresh).toBeCalledWith(
|
||||
'google',
|
||||
'REFRESH_TOKEN',
|
||||
{},
|
||||
expect.any(Function),
|
||||
);
|
||||
expect(mockResponse.send).toBeCalledTimes(1);
|
||||
expect(mockResponse.send).toBeCalledWith({
|
||||
accessToken: 'ACCESS_TOKEN',
|
||||
idToken: 'ID_TOKEN',
|
||||
expiresInSeconds: 'EXPIRES_IN',
|
||||
scope: undefined,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('refresh token cookie and scope', () => {
|
||||
const mockRequest = ({
|
||||
cookies: { 'google-refresh-token': 'REFRESH_TOKEN' },
|
||||
query: {
|
||||
scope: 'a,b',
|
||||
},
|
||||
} as unknown) as express.Request;
|
||||
|
||||
const googleAuthProvider = new GoogleAuthProvider(
|
||||
googleAuthProviderConfig,
|
||||
);
|
||||
|
||||
it('should fetch and return a new access token with scopes', () => {
|
||||
const spyRefresh = jest
|
||||
.spyOn(refresh, 'requestNewAccessToken')
|
||||
.mockImplementation((_x, _y, _z, callbackFunc) => {
|
||||
const cb = callbackFunc as Function;
|
||||
cb(undefined, 'ACCESS_TOKEN', undefined, {
|
||||
expires_in: 'EXPIRES_IN',
|
||||
id_token: 'ID_TOKEN',
|
||||
scope: 'a,b',
|
||||
});
|
||||
});
|
||||
|
||||
googleAuthProvider.refresh(mockRequest, mockResponse);
|
||||
expect(spyRefresh).toBeCalledTimes(1);
|
||||
expect(spyRefresh).toBeCalledWith(
|
||||
'google',
|
||||
'REFRESH_TOKEN',
|
||||
{ scope: 'a,b' },
|
||||
expect.any(Function),
|
||||
);
|
||||
expect(mockResponse.send).toBeCalledTimes(1);
|
||||
expect(mockResponse.send).toBeCalledWith({
|
||||
accessToken: 'ACCESS_TOKEN',
|
||||
idToken: 'ID_TOKEN',
|
||||
expiresInSeconds: 'EXPIRES_IN',
|
||||
scope: 'a,b',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,8 +15,9 @@
|
||||
*/
|
||||
|
||||
import passport from 'passport';
|
||||
import express from 'express';
|
||||
import express, { CookieOptions } from 'express';
|
||||
import { Strategy as GoogleStrategy } from 'passport-google-oauth20';
|
||||
import refresh from 'passport-oauth2-refresh';
|
||||
import {
|
||||
AuthProvider,
|
||||
AuthProviderRouteHandlers,
|
||||
@@ -25,6 +26,7 @@ import {
|
||||
import { postMessageResponse } from './../utils';
|
||||
import { InputError } from '@backstage/backend-common';
|
||||
|
||||
export const THOUSAND_DAYS_MS = 1000 * 24 * 60 * 60 * 1000;
|
||||
export class GoogleAuthProvider
|
||||
implements AuthProvider, AuthProviderRouteHandlers {
|
||||
private readonly providerConfig: AuthProviderConfig;
|
||||
@@ -53,8 +55,40 @@ export class GoogleAuthProvider
|
||||
res: express.Response,
|
||||
next: express.NextFunction,
|
||||
) {
|
||||
return passport.authenticate('google', (_, user) => {
|
||||
postMessageResponse(res, {
|
||||
return passport.authenticate('google', (err, user) => {
|
||||
if (err) {
|
||||
return postMessageResponse(res, {
|
||||
type: 'auth-result',
|
||||
error: new Error(`Google auth failed, ${err}`),
|
||||
});
|
||||
}
|
||||
|
||||
const { refreshToken } = user;
|
||||
|
||||
if (!refreshToken) {
|
||||
return postMessageResponse(res, {
|
||||
type: 'auth-result',
|
||||
error: new Error('Missing refresh token'),
|
||||
});
|
||||
}
|
||||
|
||||
delete user.refreshToken;
|
||||
|
||||
const options: CookieOptions = {
|
||||
maxAge: THOUSAND_DAYS_MS,
|
||||
secure: false,
|
||||
sameSite: 'none',
|
||||
domain: 'localhost',
|
||||
path: `/auth/${this.providerConfig.provider}`,
|
||||
httpOnly: true,
|
||||
};
|
||||
|
||||
res.cookie(
|
||||
`${this.providerConfig.provider}-refresh-token`,
|
||||
refreshToken,
|
||||
options,
|
||||
);
|
||||
return postMessageResponse(res, {
|
||||
type: 'auth-result',
|
||||
payload: user,
|
||||
});
|
||||
@@ -62,7 +96,46 @@ export class GoogleAuthProvider
|
||||
}
|
||||
|
||||
async logout(_req: express.Request, res: express.Response) {
|
||||
res.send('logout!');
|
||||
const options: CookieOptions = {
|
||||
maxAge: 0,
|
||||
secure: false,
|
||||
sameSite: 'none',
|
||||
domain: 'localhost',
|
||||
path: `/auth/${this.providerConfig.provider}`,
|
||||
httpOnly: true,
|
||||
};
|
||||
|
||||
res.cookie(`${this.providerConfig.provider}-refresh-token`, '', options);
|
||||
return res.send('logout!');
|
||||
}
|
||||
|
||||
async refresh(req: express.Request, res: express.Response) {
|
||||
const refreshToken =
|
||||
req.cookies[`${this.providerConfig.provider}-refresh-token`];
|
||||
|
||||
if (!refreshToken) {
|
||||
return res.status(401).send('Missing session cookie');
|
||||
}
|
||||
|
||||
const scope = req.query.scope?.toString() ?? '';
|
||||
const refreshTokenRequestParams = scope ? { scope } : {};
|
||||
|
||||
return refresh.requestNewAccessToken(
|
||||
this.providerConfig.provider,
|
||||
refreshToken,
|
||||
refreshTokenRequestParams,
|
||||
(err, accessToken, _refreshToken, params) => {
|
||||
if (err || !accessToken) {
|
||||
return res.status(401).send('Failed to refresh access token');
|
||||
}
|
||||
return res.send({
|
||||
accessToken,
|
||||
idToken: params.id_token,
|
||||
expiresInSeconds: params.expires_in,
|
||||
scope: params.scope,
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
strategy(): passport.Strategy {
|
||||
@@ -81,6 +154,8 @@ export class GoogleAuthProvider
|
||||
idToken: params.id_token,
|
||||
accessToken,
|
||||
refreshToken,
|
||||
scope: params.scope,
|
||||
expiresInSeconds: params.expires_in,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
@@ -24,7 +24,7 @@ export const defaultRouter = (provider: AuthProviderRouteHandlers) => {
|
||||
router.get('/handler/frame', provider.frameHandler);
|
||||
router.get('/logout', provider.logout);
|
||||
if (provider.refresh) {
|
||||
router.get('/refreshToken', provider.refresh);
|
||||
router.get('/refresh', provider.refresh);
|
||||
}
|
||||
return router;
|
||||
};
|
||||
|
||||
@@ -58,16 +58,25 @@ export type AuthProviderFactory = {
|
||||
new (providerConfig: any): AuthProvider & AuthProviderRouteHandlers;
|
||||
};
|
||||
|
||||
export type AuthInfo = {
|
||||
profile: passport.Profile;
|
||||
export type AuthInfoBase = {
|
||||
accessToken: string;
|
||||
idToken?: string;
|
||||
expiresInSeconds?: number;
|
||||
scope: string;
|
||||
};
|
||||
|
||||
export type AuthInfoWithProfile = AuthInfoBase & {
|
||||
profile: passport.Profile;
|
||||
};
|
||||
|
||||
export type AuthInfoPrivate = AuthInfoWithProfile & {
|
||||
refreshToken: string;
|
||||
};
|
||||
|
||||
export type AuthResponse =
|
||||
| {
|
||||
type: 'auth-result';
|
||||
payload: AuthInfo;
|
||||
payload: AuthInfoBase | AuthInfoWithProfile;
|
||||
}
|
||||
| {
|
||||
type: 'auth-result';
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
import express from 'express';
|
||||
import Router from 'express-promise-router';
|
||||
import passport from 'passport';
|
||||
import cookieParser from 'cookie-parser';
|
||||
import refresh from 'passport-oauth2-refresh';
|
||||
import OAuth2Strategy from 'passport-oauth2';
|
||||
import { Logger } from 'winston';
|
||||
import { providers } from './../providers/config';
|
||||
import { makeProvider } from '../providers';
|
||||
@@ -39,6 +42,9 @@ export async function createRouter(
|
||||
);
|
||||
logger.info(`Configuring provider: ${providerId}`);
|
||||
passport.use(strategy);
|
||||
if (strategy instanceof OAuth2Strategy) {
|
||||
refresh.use(strategy);
|
||||
}
|
||||
providerRouters[providerId] = providerRouter;
|
||||
}
|
||||
|
||||
@@ -52,6 +58,7 @@ export async function createRouter(
|
||||
|
||||
router.use(passport.initialize());
|
||||
router.use(passport.session());
|
||||
router.use(cookieParser());
|
||||
|
||||
for (const providerId in providerRouters) {
|
||||
if (providerRouters.hasOwnProperty(providerId)) {
|
||||
|
||||
@@ -3861,6 +3861,13 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/cookie-parser@^1.4.2":
|
||||
version "1.4.2"
|
||||
resolved "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.2.tgz#e4d5c5ffda82b80672a88a4281aaceefb1bd9df5"
|
||||
integrity sha512-uwcY8m6SDQqciHsqcKDGbo10GdasYsPCYkH3hVegj9qAah6pX5HivOnOuI3WYmyQMnOATV39zv/Ybs0bC/6iVg==
|
||||
dependencies:
|
||||
"@types/express" "*"
|
||||
|
||||
"@types/cookiejar@*":
|
||||
version "2.1.1"
|
||||
resolved "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.1.tgz#90b68446364baf9efd8e8349bb36bd3852b75b80"
|
||||
@@ -4181,7 +4188,7 @@
|
||||
resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
|
||||
integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
|
||||
|
||||
"@types/passport-google-oauth20@2.0.3":
|
||||
"@types/passport-google-oauth20@^2.0.3":
|
||||
version "2.0.3"
|
||||
resolved "https://registry.npmjs.org/@types/passport-google-oauth20/-/passport-google-oauth20-2.0.3.tgz#f554ff6d39f395acff3f1d762e54462194dac8da"
|
||||
integrity sha512-6EUEGzEg4acwowvgR/yVZIj8S2Kkwc6JmlY2/wnM1wJHNz20o7s1TIGrxnah8ymLgJasYDpy95P3TMMqlmetPw==
|
||||
@@ -4190,7 +4197,15 @@
|
||||
"@types/passport" "*"
|
||||
"@types/passport-oauth2" "*"
|
||||
|
||||
"@types/passport-oauth2@*":
|
||||
"@types/passport-oauth2-refresh@^1.1.1":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.npmjs.org/@types/passport-oauth2-refresh/-/passport-oauth2-refresh-1.1.1.tgz#cbe466d4fcac36182fd75bf55279c0b1e953c382"
|
||||
integrity sha512-Tw0JvfDPv9asgFPACd9oOGCaD/0/Uyi+QF7fmrJC74cJKC6I8N8wwhJJHyfd1N2E/qaLgTh431lhOa9jicpNdg==
|
||||
dependencies:
|
||||
"@types/oauth" "*"
|
||||
"@types/passport-oauth2" "*"
|
||||
|
||||
"@types/passport-oauth2@*", "@types/passport-oauth2@^1.4.9":
|
||||
version "1.4.9"
|
||||
resolved "https://registry.npmjs.org/@types/passport-oauth2/-/passport-oauth2-1.4.9.tgz#134007c4b505a82548c9cb19094c5baeb2205c92"
|
||||
integrity sha512-QP0q+NVQOaIu2r0e10QWkiUA0Ya5mOBHRJN0UrI+LolMLOP1/VN4EVIpJ3xVwFo+xqNFRoFvFwJhBvKnk7kpUA==
|
||||
@@ -4199,7 +4214,7 @@
|
||||
"@types/oauth" "*"
|
||||
"@types/passport" "*"
|
||||
|
||||
"@types/passport@*", "@types/passport@1.0.3":
|
||||
"@types/passport@*", "@types/passport@^1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.npmjs.org/@types/passport/-/passport-1.0.3.tgz#e459ed6c262bf0686684d1b05901be0d0b192a9c"
|
||||
integrity sha512-nyztuxtDPQv9utCzU0qW7Gl8BY2Dn8BKlYAFFyxKipFxjaVd96celbkLCV/tRqqBUZ+JB8If3UfgV8347DTo3Q==
|
||||
@@ -7241,6 +7256,14 @@ convert-source-map@^1.4.0, convert-source-map@^1.5.0, convert-source-map@^1.6.0,
|
||||
dependencies:
|
||||
safe-buffer "~5.1.1"
|
||||
|
||||
cookie-parser@^1.4.5:
|
||||
version "1.4.5"
|
||||
resolved "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.5.tgz#3e572d4b7c0c80f9c61daf604e4336831b5d1d49"
|
||||
integrity sha512-f13bPUj/gG/5mDr+xLmSxxDsB9DQiTIfhJS/sqjrmfAWiAN+x2O4i/XguTL9yDZ+/IFDanJ+5x7hC4CXT9Tdzw==
|
||||
dependencies:
|
||||
cookie "0.4.0"
|
||||
cookie-signature "1.0.6"
|
||||
|
||||
cookie-signature@1.0.6:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
|
||||
@@ -15987,14 +16010,19 @@ pascalcase@^0.1.1:
|
||||
resolved "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14"
|
||||
integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=
|
||||
|
||||
passport-google-oauth20@2.0.0:
|
||||
passport-google-oauth20@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/passport-google-oauth20/-/passport-google-oauth20-2.0.0.tgz#0d241b2d21ebd3dc7f2b60669ec4d587e3a674ef"
|
||||
integrity sha512-KSk6IJ15RoxuGq7D1UKK/8qKhNfzbLeLrG3gkLZ7p4A6DBCcv7xpyQwuXtWdpyR0+E0mwkpjY1VfPOhxQrKzdQ==
|
||||
dependencies:
|
||||
passport-oauth2 "1.x.x"
|
||||
|
||||
passport-oauth2@1.x.x:
|
||||
passport-oauth2-refresh@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/passport-oauth2-refresh/-/passport-oauth2-refresh-2.0.0.tgz#7b19c77ff3cc000819c69f6ad9e318450f57b85e"
|
||||
integrity sha512-yXvCB6nem/O+WThhiyI3TlPXpzSGY+9+hy9OTx9QF8e9GInplyRHxHaaOhFylKvnof9UmWHAufQFZk8cO1Fb2g==
|
||||
|
||||
passport-oauth2@1.x.x, passport-oauth2@^1.5.0:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.5.0.tgz#64babbb54ac46a4dcab35e7f266ed5294e3c4108"
|
||||
integrity sha512-kqBt6vR/5VlCK8iCx1/KpY42kQ+NEHZwsSyt4Y6STiNjU+wWICG1i8ucc1FapXDGO15C5O5VZz7+7vRzrDPXXQ==
|
||||
@@ -16010,7 +16038,7 @@ passport-strategy@1.x.x:
|
||||
resolved "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz#b5539aa8fc225a3d1ad179476ddf236b440f52e4"
|
||||
integrity sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=
|
||||
|
||||
passport@0.4.1:
|
||||
passport@^0.4.1:
|
||||
version "0.4.1"
|
||||
resolved "https://registry.npmjs.org/passport/-/passport-0.4.1.tgz#941446a21cb92fc688d97a0861c38ce9f738f270"
|
||||
integrity sha512-IxXgZZs8d7uFSt3eqNjM9NQ3g3uQCW5avD8mRNoXV99Yig50vjuaez6dQK2qC0kVWPRTujxY0dWgGfT09adjYg==
|
||||
|
||||
Reference in New Issue
Block a user