change code to use search params instead of cookie
Signed-off-by: Stephen Glass <stephen@stephen.glass>
This commit is contained in:
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Response } from 'express';
|
||||
import { serializeError } from '@backstage/errors';
|
||||
|
||||
const ONE_MINUTE_MS = 60 * 1000;
|
||||
|
||||
const AUTH_ERROR_COOKIE = 'auth-error';
|
||||
|
||||
function configureAuthErrorCookie(apiUrl: string, appOrigin: string) {
|
||||
const { hostname: domain, pathname: path, protocol } = new URL(apiUrl);
|
||||
const secure = protocol === 'https:';
|
||||
|
||||
// For situations where the auth-backend is running on a
|
||||
// different domain than the app, we set the SameSite attribute
|
||||
// to 'none' to allow third-party access to the cookie, but
|
||||
// only if it's in a secure context (https).
|
||||
let sameSite: 'lax' | 'none' = 'lax';
|
||||
if (new URL(appOrigin).hostname !== domain && secure) {
|
||||
sameSite = 'none';
|
||||
}
|
||||
|
||||
return { domain, path, secure, sameSite };
|
||||
}
|
||||
|
||||
export function createAuthErrorCookie(
|
||||
res: Response,
|
||||
origin: string,
|
||||
options: {
|
||||
error: Error;
|
||||
apiUrl: string;
|
||||
},
|
||||
) {
|
||||
const { error, apiUrl } = options;
|
||||
const jsonData = serializeError(error);
|
||||
|
||||
res.cookie(AUTH_ERROR_COOKIE, jsonData, {
|
||||
maxAge: ONE_MINUTE_MS,
|
||||
httpOnly: true,
|
||||
...configureAuthErrorCookie(apiUrl, origin),
|
||||
});
|
||||
}
|
||||
@@ -743,7 +743,7 @@ describe('createOAuthRouteHandlers', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should set cookie and redirect on caught error', async () => {
|
||||
it('should set error search param and redirect on caught error', async () => {
|
||||
const app = wrapInApp(createOAuthRouteHandlers(baseConfig));
|
||||
const res = await request(app)
|
||||
.get('/my-provider/handler/frame')
|
||||
@@ -756,15 +756,21 @@ describe('createOAuthRouteHandlers', () => {
|
||||
}),
|
||||
});
|
||||
|
||||
// redirects on error with auth error cookie
|
||||
// Check if it redirects on error
|
||||
expect(res.status).toBe(302);
|
||||
const setCookieHeader = res.header['set-cookie'];
|
||||
expect(setCookieHeader).toBeDefined();
|
||||
|
||||
const authErrorCookie = setCookieHeader.find((cookie: string) =>
|
||||
cookie.startsWith('auth-error='),
|
||||
// Extract the redirect URL from the location header
|
||||
const redirectLocation = res.header.location;
|
||||
expect(redirectLocation).toBeDefined();
|
||||
|
||||
// Create a URL object from the redirect location
|
||||
const redirectUrl = new URL(redirectLocation);
|
||||
|
||||
// Verify that the 'error' search param is set with the encoded error message
|
||||
const errorMessage = redirectUrl.searchParams.get('error');
|
||||
expect(errorMessage).toBe(
|
||||
encodeURIComponent('Auth response is missing cookie nonce'),
|
||||
);
|
||||
expect(authErrorCookie).toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -42,7 +42,6 @@ import {
|
||||
import { OAuthAuthenticator, OAuthAuthenticatorResult } from './types';
|
||||
import { Config } from '@backstage/config';
|
||||
import { CookieScopeManager } from './CookieScopeManager';
|
||||
import { createAuthErrorCookie } from './createAuthErrorCookie';
|
||||
|
||||
/** @public */
|
||||
export interface OAuthRouteHandlersOptions<TProfile> {
|
||||
@@ -252,13 +251,8 @@ export function createOAuthRouteHandlers<TProfile>(
|
||||
: new Error('Encountered invalid error'); // Being a bit safe and not forwarding the bad value
|
||||
|
||||
if (state?.flow === 'redirect' && state?.redirectUrl) {
|
||||
createAuthErrorCookie(res, state?.redirectUrl, {
|
||||
error: { name, message },
|
||||
apiUrl: `${baseUrl}/.backstage/error`,
|
||||
});
|
||||
|
||||
const redirectUrl = new URL(state.redirectUrl);
|
||||
redirectUrl.searchParams.set('error', 'true');
|
||||
redirectUrl.searchParams.set('error', encodeURIComponent(message));
|
||||
|
||||
// set the error in a cookie and redirect user back to sign in where the error can be rendered
|
||||
res.redirect(redirectUrl.toString());
|
||||
|
||||
Reference in New Issue
Block a user