Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support for forwardAuth #580

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions overseerr-api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ info:

- **Cookie Authentication**: A valid sign-in to the `/auth/plex` or `/auth/local` will generate a valid authentication cookie.
- **API Key Authentication**: Sign-in is also possible by passing an `X-Api-Key` header along with a valid API Key generated by Overseerr.
- **ForwardAuth Authentication**: Sign-in is also possible by passing an `X-Forwarded-User` header containing user's e-mail.
tags:
- name: public
description: Public API endpoints requiring no authentication.
Expand Down Expand Up @@ -171,6 +172,9 @@ components:
defaultPermissions:
type: number
example: 32
enableForwardAuth:
type: boolean
example: true
PlexLibrary:
type: object
properties:
Expand Down Expand Up @@ -1921,6 +1925,10 @@ components:
type: apiKey
in: header
name: X-Api-Key
forwardAuth:
type: apiKey
in: header
name: X-Forwarded-User

paths:
/status:
Expand Down Expand Up @@ -6804,3 +6812,4 @@ paths:
security:
- cookieAuth: []
- apiKey: []
- forwardAuth: []
26 changes: 25 additions & 1 deletion server/middleware/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,19 @@ export const checkUser: Middleware = async (req, _res, next) => {
}

user = await userRepository.findOne({ where: { id: userId } });
} else if (req.header('x-forwarded-user')) {
const userRepository = getRepository(User);
user = await userRepository.findOne({
where: { email: req.header('x-forwarded-user') },
});

if (user) {
req.user = user;
}

req.locale = user?.settings?.locale
? user.settings.locale
: settings.main.locale;
} else if (req.session?.userId) {
const userRepository = getRepository(User);

Expand All @@ -44,7 +57,18 @@ export const isAuthenticated = (
permissions?: Permission | Permission[],
options?: PermissionCheckOptions
): Middleware => {
const authMiddleware: Middleware = (req, res, next) => {
const authMiddleware: Middleware = async (req, res, next) => {
if (req.header('x-forwarded-user')) {
const userRepository = getRepository(User);
const user = await userRepository.findOne({
where: { email: req.header('x-forwarded-user') },
});

if (user) {
req.user = user;
}
}

if (!req.user || !req.user.hasPermission(permissions ?? 0, options)) {
res.status(403).json({
status: 403,
Expand Down
6 changes: 2 additions & 4 deletions src/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { SettingsProvider } from '@app/context/SettingsContext';
import { UserContext } from '@app/context/UserContext';
import type { User } from '@app/hooks/useUser';
import '@app/styles/globals.css';
import { getRequestHeaders } from '@app/utils/localRequestHelper';
import { polyfillIntl } from '@app/utils/polyfillIntl';
import { MediaServerType } from '@server/constants/server';
import type { PublicSettingsResponse } from '@server/interfaces/api/settingsInterfaces';
Expand Down Expand Up @@ -215,10 +216,7 @@ CoreApp.getInitialProps = async (initialProps) => {
const response = await axios.get<User>(
`http://localhost:${process.env.PORT || 5055}/api/v1/auth/me`,
{
headers:
ctx.req && ctx.req.headers.cookie
? { cookie: ctx.req.headers.cookie }
: undefined,
headers: getRequestHeaders(ctx),
}
);
user = response.data;
Expand Down
5 changes: 2 additions & 3 deletions src/pages/collection/[collectionId]/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import CollectionDetails from '@app/components/CollectionDetails';
import { getRequestHeaders } from '@app/utils/localRequestHelper';
import type { Collection } from '@server/models/Collection';
import axios from 'axios';
import type { GetServerSideProps, NextPage } from 'next';
Expand All @@ -19,9 +20,7 @@ export const getServerSideProps: GetServerSideProps<
ctx.query.collectionId
}`,
{
headers: ctx.req?.headers?.cookie
? { cookie: ctx.req.headers.cookie }
: undefined,
headers: getRequestHeaders(ctx),
}
);

Expand Down
5 changes: 2 additions & 3 deletions src/pages/movie/[movieId]/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import MovieDetails from '@app/components/MovieDetails';
import { getRequestHeaders } from '@app/utils/localRequestHelper';
import type { MovieDetails as MovieDetailsType } from '@server/models/Movie';
import axios from 'axios';
import type { GetServerSideProps, NextPage } from 'next';
Expand All @@ -19,9 +20,7 @@ export const getServerSideProps: GetServerSideProps<MoviePageProps> = async (
ctx.query.movieId
}`,
{
headers: ctx.req?.headers?.cookie
? { cookie: ctx.req.headers.cookie }
: undefined,
headers: getRequestHeaders(ctx),
}
);

Expand Down
5 changes: 2 additions & 3 deletions src/pages/tv/[tvId]/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import TvDetails from '@app/components/TvDetails';
import { getRequestHeaders } from '@app/utils/localRequestHelper';
import type { TvDetails as TvDetailsType } from '@server/models/Tv';
import axios from 'axios';
import type { GetServerSideProps, NextPage } from 'next';
Expand All @@ -17,9 +18,7 @@ export const getServerSideProps: GetServerSideProps<TvPageProps> = async (
const response = await axios.get<TvDetailsType>(
`http://localhost:${process.env.PORT || 5055}/api/v1/tv/${ctx.query.tvId}`,
{
headers: ctx.req?.headers?.cookie
? { cookie: ctx.req.headers.cookie }
: undefined,
headers: getRequestHeaders(ctx),
}
);

Expand Down
18 changes: 18 additions & 0 deletions src/utils/localRequestHelper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { NextPageContext } from 'next/dist/shared/lib/utils';
import type { GetServerSidePropsContext, PreviewData } from 'next/types';
import type { ParsedUrlQuery } from 'querystring';

export const getRequestHeaders = (
ctx: NextPageContext | GetServerSidePropsContext<ParsedUrlQuery, PreviewData>
) => {
return ctx.req && ctx.req.headers
? {
...(ctx.req.headers.cookie && {
cookie: ctx.req.headers.cookie,
}),
...(ctx.req.headers['x-forwarded-user'] && {
'x-forwarded-user': ctx.req.headers['x-forwarded-user'] as string,
}),
}
: undefined;
};