Skip to content
Closed
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
23 changes: 22 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,28 @@
- Mobile-friendly design, for when you need to approve requests on the go!
- Support for watchlisting & blacklisting media.

With more features on the way! Check out our [issue tracker](https://github.com/fallenbagel/jellyseerr/issues) to see the features which have already been requested.
### Single Sign-On (SSO) Support

When configuring SSO, please note:

- For OpenID Configuration URL:

- Remove `/.well-known/openid-configuration` from the URL
- Example: Use `https://auth.example.com` instead of `https://auth.example.com/.well-known/openid-configuration`

- For Redirect URIs:

- Must end with `/login/oidc/callback`
- Example: `http://jellyseerr.example.com/login/oidc/callback`

- Account Matching:

- Users are matched to existing Jellyseerr accounts based on email address
- If an SSO user's email matches an existing Jellyseerr account, they will be logged in as that user
- Example: If a Jellyseerr admin account uses `admin@example.com` and an SSO user with the same email logs in, they will receive admin privileges
- Consider this when setting up SSO to ensure proper access control

With more features on the way! Check out our [issue tracker](https://github.com/fallenbagel/jellyseerr/issues) to see the features which have already been requested.

## Getting Started

Expand Down
2 changes: 1 addition & 1 deletion charts/jellyseerr-chart/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ kubeVersion: ">=1.23.0-0"
name: jellyseerr-chart
description: Jellyseerr helm chart for Kubernetes
type: application
version: 2.3.2
version: 2.5.2
appVersion: "2.5.1"
maintainers:
- name: Jellyseerr
Expand Down
103 changes: 103 additions & 0 deletions jellyseerr-api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,36 @@ components:
type: string
streamingRegion:
type: string
OidcSettings:
type: object
properties:
providerName:
type: string
example: Keycloak
providerUrl:
type: string
example: https://auth.example.com
clientId:
type: string
example: your-client-id
clientSecret:
type: string
example: your-client-secret
userIdentifier:
type: string
example: email
requiredClaims:
type: string
example: email_verified
scopes:
type: string
example: id email
matchJellyfinUsername:
type: boolean
example: false
automaticLogin:
type: boolean
example: false
MainSettings:
type: object
properties:
Expand Down Expand Up @@ -3782,6 +3812,79 @@ paths:
type: string
required:
- password
/auth/oidc-login:
get:
security: []
summary: Redirect to the OpenID Connect provider
description: Constructs the redirect URL to the OpenID Connect provider, and redirects the user to it.
tags:
- auth
responses:
'302':
description: Redirect to the authentication url for the OpenID Connect provider
headers:
Location:
schema:
type: string
example: https://example.com/auth/oidc/callback?response_type=code&client_id=client_id&redirect_uri=https%3A%2F%2Fexample.com%2Fauth%2Foidc%2Fcallback&scope=openid%20email&state=state
Set-Cookie:
schema:
type: string
example: 'oidc-state=123456789; HttpOnly; max-age=60000; Secure'
/auth/oidc-callback:
get:
security: []
summary: The callback endpoint for the OpenID Connect provider redirect
description: Takes the `code` and `state` parameters from the OpenID Connect provider, and exchanges them for a token.
x-allow-unknown-query-parameters: true
parameters:
- in: query
name: code
required: true
schema:
type: string
example: '123456789'
- in: query
name: state
required: true
schema:
type: string
example: '123456789'
- in: cookie
name: oidc-state
required: true
schema:
type: string
example: '123456789'
tags:
- auth
responses:
'302':
description: A redirect to the home page if successful or back to the login page if not
headers:
Location:
schema:
type: string
example: /
Set-Cookie:
schema:
type: string
example: 'oidc-state=deleted; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT'
/auth/oidc-logout:
get:
security: []
summary: Redirect to the OpenID Connect provider logout page
description: Determines the logout redirect URL for to the OpenID Connect provider, and redirects the user to it.
tags:
- auth
responses:
'302':
description: Redirect to the logout url for the OpenID Connect provider
headers:
Location:
schema:
type: string
example: https://example.com/auth/oidc/invalidate_session
/user:
get:
summary: Get all users
Expand Down
4 changes: 2 additions & 2 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

39 changes: 39 additions & 0 deletions scripts/add-oidc-column.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import Database from 'better-sqlite3';
import path from 'path';

try {
const db = new Database(path.join(process.cwd(), 'config/db/db.sqlite3'));

// Check if column exists
const columnExists = db.prepare(`
SELECT COUNT(*) as count
FROM pragma_table_info('user')
WHERE name = 'oidcId'
`).get().count > 0;

if (columnExists) {
console.log('oidcId column already exists in user table');
db.close();
process.exit(0);
}

// Create backup
db.backup(path.join(process.cwd(), 'config/db/db.sqlite3.backup'))
.then(() => {
console.log('Database backup created');

// Add the column
db.exec('ALTER TABLE user ADD COLUMN oidcId VARCHAR;');
console.log('Added oidcId column to user table');

db.close();
})
.catch((err) => {
console.error('Backup failed:', err);
db.close();
process.exit(1);
});
} catch (err) {
console.error('Failed to modify database:', err);
process.exit(1);
}
28 changes: 28 additions & 0 deletions scripts/check-oidc-column.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import Database from 'better-sqlite3';
import path from 'path';

try {
const db = new Database(path.join(process.cwd(), 'config/db/db.sqlite3'));

// Get table info
const tableInfo = db.prepare(`
SELECT * FROM pragma_table_info('user')
`).all();

console.log('User table structure:');
console.table(tableInfo);

// Check for any existing OIDC users
const oidcUsers = db.prepare(`
SELECT id, email, oidcId
FROM user
WHERE oidcId IS NOT NULL
`).all();

console.log('\nOIDC Users:', oidcUsers.length ? oidcUsers : 'None found');

db.close();
} catch (err) {
console.error('Failed to read database:', err);
process.exit(1);
}
1 change: 1 addition & 0 deletions server/constants/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ export enum UserType {
LOCAL = 2,
JELLYFIN = 3,
EMBY = 4,
OIDC = 5 // Add this line
}
3 changes: 3 additions & 0 deletions server/entity/User.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,9 @@ export class User {

public warnings: string[] = [];

@Column({ nullable: true })
public oidcId?: string;

constructor(init?: Partial<User>) {
Object.assign(this, init);
}
Expand Down
Loading