mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-05-28 18:26:54 +00:00
chore: delete auth0 PKCE routes (#53820)
This commit is contained in:
committed by
GitHub
parent
71ec874986
commit
056ac85e52
@@ -22,7 +22,6 @@
|
||||
"express-rate-limit": "^6.7.0",
|
||||
"fast-uri": "2.3.0",
|
||||
"fastify": "4.24.3",
|
||||
"fastify-auth0-verify": "1.2.1",
|
||||
"fastify-plugin": "4.5.1",
|
||||
"joi": "17.12.1",
|
||||
"jsonwebtoken": "9.0.2",
|
||||
|
||||
@@ -18,11 +18,9 @@ import Fastify, {
|
||||
RawRequestDefaultExpression,
|
||||
RawServerDefault
|
||||
} from 'fastify';
|
||||
import fastifyAuth0 from 'fastify-auth0-verify';
|
||||
|
||||
import prismaPlugin from './db/prisma';
|
||||
import cors from './plugins/cors';
|
||||
import jwtAuthz from './plugins/fastify-jwt-authz';
|
||||
import { NodemailerProvider } from './plugins/mail-providers/nodemailer';
|
||||
import { SESProvider } from './plugins/mail-providers/ses';
|
||||
import mailer from './plugins/mailer';
|
||||
@@ -30,7 +28,6 @@ import redirectWithMessage from './plugins/redirect-with-message';
|
||||
import security from './plugins/security';
|
||||
import sessionAuth from './plugins/session-auth';
|
||||
import {
|
||||
auth0Routes,
|
||||
devLoginCallback,
|
||||
devLegacyAuthRoutes,
|
||||
mobileAuth0Routes
|
||||
@@ -44,8 +41,6 @@ import { statusRoute } from './routes/status';
|
||||
import { userGetRoutes, userRoutes } from './routes/user';
|
||||
import {
|
||||
API_LOCATION,
|
||||
AUTH0_AUDIENCE,
|
||||
AUTH0_DOMAIN,
|
||||
COOKIE_DOMAIN,
|
||||
EMAIL_PROVIDER,
|
||||
FCC_ENABLE_DEV_LOGIN_MODE,
|
||||
@@ -194,17 +189,8 @@ export const build = async (
|
||||
fastify.log.info(`Swagger UI available at ${API_LOCATION}/documentation`);
|
||||
}
|
||||
|
||||
// Auth0 plugin
|
||||
void fastify.register(fastifyAuth0, {
|
||||
domain: AUTH0_DOMAIN,
|
||||
audience: AUTH0_AUDIENCE
|
||||
});
|
||||
void fastify.register(jwtAuthz);
|
||||
void fastify.register(sessionAuth);
|
||||
|
||||
void fastify.register(prismaPlugin);
|
||||
|
||||
void fastify.register(auth0Routes, { prefix: '/auth' });
|
||||
void fastify.register(mobileAuth0Routes);
|
||||
if (FCC_ENABLE_DEV_LOGIN_MODE) {
|
||||
void fastify.register(devLoginCallback, { prefix: '/auth' });
|
||||
|
||||
@@ -1,290 +0,0 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Ethan Arrowood
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
import Fastify from 'fastify';
|
||||
import jwtAuthz from './fastify-jwt-authz';
|
||||
|
||||
interface ErrorResponse {
|
||||
statusCode: number;
|
||||
error: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
describe('fastify-jwt-authz', () => {
|
||||
test('should decorate request instance with jwtAuthz method', async () => {
|
||||
const fastify = Fastify();
|
||||
await fastify.register(jwtAuthz);
|
||||
|
||||
fastify.get('/', req => {
|
||||
expect(req).toHaveProperty('jwtAuthz');
|
||||
expect(req.jwtAuthz).toBeInstanceOf(Function);
|
||||
return { foo: 'bar' };
|
||||
});
|
||||
|
||||
fastify.listen({ port: 0 }, function () {
|
||||
fastify.server.unref();
|
||||
});
|
||||
|
||||
const res = await fastify.inject({
|
||||
method: 'GET',
|
||||
url: '/'
|
||||
});
|
||||
|
||||
expect(res.statusCode).toEqual(200);
|
||||
});
|
||||
|
||||
test('should throw an error "Scopes cannot be empty" with an empty scopes parameter', async () => {
|
||||
const fastify = Fastify();
|
||||
await fastify.register(jwtAuthz);
|
||||
|
||||
fastify.get(
|
||||
'/test2',
|
||||
{
|
||||
preHandler: function (request, _reply, done) {
|
||||
void request.jwtAuthz([], done);
|
||||
}
|
||||
},
|
||||
function () {
|
||||
return { foo: 'bar' };
|
||||
}
|
||||
);
|
||||
|
||||
fastify.listen({ port: 0 }, function () {
|
||||
fastify.server.unref();
|
||||
});
|
||||
|
||||
const res = await fastify.inject({
|
||||
method: 'GET',
|
||||
url: '/test2'
|
||||
});
|
||||
const resData: ErrorResponse = res.json();
|
||||
|
||||
expect(res.statusCode).toEqual(500);
|
||||
expect(resData.message).toEqual('Scopes cannot be empty');
|
||||
});
|
||||
|
||||
test('should throw an error "request.user does not exist" non existing request.user', async () => {
|
||||
const fastify = Fastify();
|
||||
await fastify.register(jwtAuthz);
|
||||
|
||||
fastify.get(
|
||||
'/test3',
|
||||
{
|
||||
preHandler: function (request, _reply, done) {
|
||||
void request.jwtAuthz(['baz'], done);
|
||||
}
|
||||
},
|
||||
function () {
|
||||
return { foo: 'bar' };
|
||||
}
|
||||
);
|
||||
|
||||
fastify.listen({ port: 0 }, function () {
|
||||
fastify.server.unref();
|
||||
});
|
||||
|
||||
const res = await fastify.inject({
|
||||
method: 'GET',
|
||||
url: '/test3'
|
||||
});
|
||||
const resData: ErrorResponse = res.json();
|
||||
|
||||
expect(res.statusCode).toEqual(500);
|
||||
expect(resData.message).toEqual('request.user does not exist');
|
||||
});
|
||||
|
||||
test('should throw an error "request.user.scope must be a string"', async () => {
|
||||
const fastify = Fastify();
|
||||
await fastify.register(jwtAuthz);
|
||||
|
||||
fastify.get(
|
||||
'/test4',
|
||||
{
|
||||
preHandler: function (request, _reply, done) {
|
||||
request.user = {
|
||||
name: 'sample',
|
||||
scope: 123
|
||||
};
|
||||
void request.jwtAuthz(['baz'], done);
|
||||
}
|
||||
},
|
||||
function () {
|
||||
return { foo: 'bar' };
|
||||
}
|
||||
);
|
||||
|
||||
fastify.listen({ port: 0 }, function () {
|
||||
fastify.server.unref();
|
||||
});
|
||||
|
||||
const res = await fastify.inject({
|
||||
method: 'GET',
|
||||
url: '/test4'
|
||||
});
|
||||
const resData: ErrorResponse = res.json();
|
||||
|
||||
expect(res.statusCode).toEqual(500);
|
||||
expect(resData.message).toEqual('request.user.scope must be a string');
|
||||
});
|
||||
|
||||
test('should throw an error "Insufficient scope"', async () => {
|
||||
const fastify = Fastify();
|
||||
await fastify.register(jwtAuthz);
|
||||
|
||||
fastify.get(
|
||||
'/test5',
|
||||
{
|
||||
preHandler: function (request, _reply, done) {
|
||||
request.user = {
|
||||
name: 'sample',
|
||||
scope: 'baz'
|
||||
};
|
||||
void request.jwtAuthz(['foo'], done);
|
||||
}
|
||||
},
|
||||
function () {
|
||||
return { foo: 'bar' };
|
||||
}
|
||||
);
|
||||
|
||||
fastify.listen({ port: 0 }, function () {
|
||||
fastify.server.unref();
|
||||
});
|
||||
|
||||
const res = await fastify.inject({
|
||||
method: 'GET',
|
||||
url: '/test5'
|
||||
});
|
||||
const resData: ErrorResponse = res.json();
|
||||
|
||||
expect(res.statusCode).toEqual(500);
|
||||
expect(resData.message).toEqual('Insufficient scope');
|
||||
});
|
||||
|
||||
test('should verify user scope', async () => {
|
||||
const fastify = Fastify();
|
||||
await fastify.register(jwtAuthz);
|
||||
|
||||
fastify.get(
|
||||
'/test6',
|
||||
{
|
||||
preHandler: function (request, _reply, done) {
|
||||
request.user = {
|
||||
name: 'sample',
|
||||
scope: 'user manager'
|
||||
};
|
||||
void request.jwtAuthz(['user'], done);
|
||||
}
|
||||
},
|
||||
function () {
|
||||
return { foo: 'bar' };
|
||||
}
|
||||
);
|
||||
|
||||
fastify.listen({ port: 0 }, function () {
|
||||
fastify.server.unref();
|
||||
});
|
||||
|
||||
const res = await fastify.inject({
|
||||
method: 'GET',
|
||||
url: '/test6'
|
||||
});
|
||||
|
||||
const resData: { foo: string } = res.json();
|
||||
|
||||
expect(res.statusCode).toEqual(200);
|
||||
expect(resData.foo).toEqual('bar');
|
||||
});
|
||||
|
||||
test('should throw an error when there is no callback', async () => {
|
||||
const fastify = Fastify();
|
||||
await fastify.register(jwtAuthz);
|
||||
|
||||
fastify.get(
|
||||
'/test7',
|
||||
{
|
||||
preHandler: function (request, _reply, done) {
|
||||
request.user = {
|
||||
name: 'sample',
|
||||
scope: 123
|
||||
};
|
||||
|
||||
request.jwtAuthz(['baz']);
|
||||
done();
|
||||
}
|
||||
},
|
||||
function () {
|
||||
return { foo: 'bar' };
|
||||
}
|
||||
);
|
||||
|
||||
fastify.listen({ port: 0 }, function () {
|
||||
fastify.server.unref();
|
||||
});
|
||||
|
||||
const res = await fastify.inject({
|
||||
method: 'GET',
|
||||
url: '/test7'
|
||||
});
|
||||
const resData: ErrorResponse = res.json();
|
||||
|
||||
expect(res.statusCode).toEqual(500);
|
||||
expect(resData.message).toEqual('request.user.scope must be a string');
|
||||
});
|
||||
|
||||
test('should verify user scope when there is no callback', async () => {
|
||||
const fastify = Fastify();
|
||||
await fastify.register(jwtAuthz);
|
||||
|
||||
fastify.get(
|
||||
'/test8',
|
||||
{
|
||||
preHandler: function (request, _reply, done) {
|
||||
request.user = {
|
||||
name: 'sample',
|
||||
scope: 'user manager'
|
||||
};
|
||||
request.jwtAuthz(['user']);
|
||||
done();
|
||||
}
|
||||
},
|
||||
function () {
|
||||
return { foo: 'bar' };
|
||||
}
|
||||
);
|
||||
|
||||
fastify.listen({ port: 0 }, function () {
|
||||
fastify.server.unref();
|
||||
});
|
||||
|
||||
const res = await fastify.inject({
|
||||
method: 'GET',
|
||||
url: '/test8'
|
||||
});
|
||||
const resData: { foo: string } = res.json();
|
||||
|
||||
expect(res.statusCode).toEqual(200);
|
||||
expect(resData.foo).toEqual('bar');
|
||||
});
|
||||
});
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Ethan Arrowood
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
import { FastifyPluginCallback, FastifyRequest } from 'fastify';
|
||||
import fp from 'fastify-plugin';
|
||||
|
||||
export interface UserObject {
|
||||
scope?: string;
|
||||
}
|
||||
|
||||
interface JwtAuthz {
|
||||
(scopes: string[], callback?: (err?: Error) => void): void;
|
||||
}
|
||||
|
||||
const fastifyJwtAuthz: FastifyPluginCallback = (fastify, _opts, done) => {
|
||||
fastify.decorateRequest('jwtAuthz', jwtAuthz);
|
||||
|
||||
function checkScopes(user: UserObject, scopes: string[]) {
|
||||
if (scopes.length === 0) return Error('Scopes cannot be empty');
|
||||
|
||||
if (!user) return Error('request.user does not exist');
|
||||
|
||||
if (typeof user.scope !== 'string')
|
||||
return Error('request.user.scope must be a string');
|
||||
|
||||
const userScopes = user.scope.split(' ');
|
||||
const sufficientScope = scopes.some(scope => userScopes.includes(scope));
|
||||
|
||||
if (!sufficientScope) return Error('Insufficient scope');
|
||||
}
|
||||
|
||||
function jwtAuthz(
|
||||
this: FastifyRequest,
|
||||
scopes: string[],
|
||||
callback?: (err?: Error) => void
|
||||
) {
|
||||
const err = checkScopes(this.user as UserObject, scopes);
|
||||
if (callback) return callback(err);
|
||||
if (err) throw err;
|
||||
}
|
||||
|
||||
done();
|
||||
};
|
||||
|
||||
declare module 'fastify' {
|
||||
interface FastifyRequest {
|
||||
jwtAuthz: JwtAuthz;
|
||||
}
|
||||
}
|
||||
|
||||
export default fp(fastifyJwtAuthz);
|
||||
@@ -78,28 +78,6 @@ export const devLoginCallback: FastifyPluginCallback = (
|
||||
done();
|
||||
};
|
||||
|
||||
/**
|
||||
* Route handler for Auth0 authentication.
|
||||
*
|
||||
* @param fastify The Fastify instance.
|
||||
* @param _options Options passed to the plugin via `fastify.register(plugin, options)`.
|
||||
* @param done Callback to signal that the logic has completed.
|
||||
*/
|
||||
// TODO: 1) use POST 2) make sure we prevent login CSRF
|
||||
export const auth0Routes: FastifyPluginCallback = (fastify, _options, done) => {
|
||||
fastify.addHook('onRequest', fastify.authenticate);
|
||||
|
||||
fastify.get('/auth0/callback', async req => {
|
||||
const email = await getEmailFromAuth0(req);
|
||||
|
||||
const { id } = await findOrCreateUser(fastify, email);
|
||||
req.session.user = { id };
|
||||
await req.session.save();
|
||||
});
|
||||
|
||||
done();
|
||||
};
|
||||
|
||||
/**
|
||||
* Route handler for Mobile authentication.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user