Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
6436f25
feat(api): enhance OIDC redirect URI handling in service and tests
elibosley Aug 24, 2025
f7587de
fix(api): ensure requestInfo is correctly typed in RestController
elibosley Aug 24, 2025
661768d
test(api): add integration tests for OidcAuthService with enhanced lo…
elibosley Aug 24, 2025
35ddd36
feat(api): enhance logging in OidcAuthService and OidcValidationService
elibosley Aug 24, 2025
7b6b9cd
fix(api): make OIDC issuer field optional in OidcProvider model
elibosley Aug 24, 2025
62b0e5f
feat: enhance oidc logging on config (#1621)
elibosley Aug 24, 2025
ce29759
feat(api): enhance logging configuration with custom formatters
elibosley Aug 24, 2025
66845cc
fix: remove unused authUrl variable
elibosley Aug 24, 2025
b5c04ab
feat(api): enhance pubsub and logging functionality
elibosley Aug 24, 2025
f036382
feat(api): enhance error logging in OidcAuthService
elibosley Aug 24, 2025
b7be176
refactor(api): streamline OidcAuthService and SubscriptionManagerService
elibosley Aug 24, 2025
067432d
feat(api): enhance logging and filtering in LogsService
elibosley Aug 24, 2025
06ffc66
feat(Logs): update refreshLogContent to be asynchronous and ensure la…
elibosley Aug 24, 2025
68ac663
refactor(Logs): rename refreshLogContent state clearing function for …
elibosley Aug 24, 2025
b7a75c9
refactor(Logs): improve log formatting and cleanup in SingleLogViewer
elibosley Aug 24, 2025
61921f6
refactor(api): improve logging and error handling in OidcAuthService …
elibosley Aug 24, 2025
120d0ed
feat(api): implement OIDC request handling utilities and enhance stat…
elibosley Aug 24, 2025
171f60f
feat(api): enhance OIDC state management with caching and async opera…
elibosley Aug 24, 2025
c7e01d1
test(api): enhance OidcStateExtractor tests with async handling and c…
elibosley Aug 24, 2025
74304e8
fix(api): ensure async state validation in OidcAuthService
elibosley Aug 24, 2025
e7e0c6f
test(api): integrate NestJS cache module in OIDC state service tests
elibosley Aug 24, 2025
573539a
refactor(api): remove unused CACHE_MANAGER import in OIDC state servi…
elibosley Aug 24, 2025
1ff5867
feat(api): integrate NestJS CacheModule for improved state management…
elibosley Aug 24, 2025
5e61838
refactor(api): streamline state validation and enhance logging in OID…
elibosley Aug 24, 2025
39ffcf5
test(api): add test to ensure state validation occurs only once durin…
elibosley Aug 24, 2025
56f6bca
fix(api): handle missing client state in OIDC token exchange
elibosley Aug 24, 2025
72c96ec
feat(api): enhance OIDC flow by preserving redirect URI with custom p…
elibosley Aug 24, 2025
92a32de
test(api): add unit tests for OIDC authorization flow in RestController
elibosley Aug 24, 2025
fe01463
chore(api): remove unused PUBSUB_CHANNEL import in logs.resolver.ts
elibosley Aug 25, 2025
92ea141
refactor(api): improve logging in OidcAuthService for claim evaluations
elibosley Aug 25, 2025
da55e1a
refactor(api): remove filter parameter from log file queries and subs…
elibosley Aug 25, 2025
954440c
refactor(api): enhance logging in OidcAuthService integration tests
elibosley Aug 25, 2025
29cadbd
feat: fix issue with import
elibosley Aug 27, 2025
49899e4
refactor(OidcDebugLogs): simplify log file path declaration
elibosley Aug 27, 2025
5b3c1fd
refactor(api): implement ErrorExtractor for improved error handling a…
elibosley Aug 27, 2025
f4d5632
refactor(api): update logging level for authorization parameters in O…
elibosley Aug 27, 2025
7380d78
feat(api): enhance OidcAuthService with request header validation for…
elibosley Aug 29, 2025
71335b1
chore(api): update generated GraphQL schema and versioning
elibosley Aug 29, 2025
917ee4b
feat(api): enhance OidcAuthService and SingleLogViewer with ANSI supp…
elibosley Aug 29, 2025
af2d45e
fix: claude MD
elibosley Aug 29, 2025
20bb830
refactor(CLAUDE.md, SingleLogViewer.vue): update guidelines and enhan…
elibosley Aug 29, 2025
639fbc5
fix: lint
elibosley Aug 29, 2025
8e4776d
fix: recall subscribe to more
elibosley Aug 29, 2025
3afcfec
feat(api): add configuration download feature and enhance OIDC manage…
elibosley Aug 29, 2025
73fb4ae
refactor(SingleLogViewer.test.ts): remove performance timing tests fo…
elibosley Aug 29, 2025
b2f5567
feat(api): enhance configuration file management and OIDC integration
elibosley Aug 29, 2025
ef17d65
fix: type check
elibosley Aug 29, 2025
37db067
fix: web component registration for configs
elibosley Aug 29, 2025
150e828
fix: update ConfigDownload component tag and register in Nuxt config
elibosley Aug 29, 2025
b1a1a0c
fix: config download manager route
elibosley Aug 29, 2025
89fd66b
fix: allow https
elibosley Aug 29, 2025
af36a41
fix: update redirect_uri logging in OIDC flow
elibosley Aug 29, 2025
796124c
fix: improve JSON formatting in ConfigDownloadManager
elibosley Aug 29, 2025
685a3ad
fix: enhance OIDC redirect_uri validation and logging
elibosley Aug 29, 2025
450e0ba
fix: add escape-html dependency and enhance redirect_uri error handling
elibosley Aug 29, 2025
e43a6ab
fix: refine createSubscription parameter type in pubsub
elibosley Aug 30, 2025
ea711ec
refactor: remove ConfigDownload functionality and related components
elibosley Aug 30, 2025
a146b29
refactor: enhance log file handling and introduce LogWatcherManager
elibosley Aug 30, 2025
fe78cfb
refactor: update OIDC service methods to use parameter objects
elibosley Aug 30, 2025
8cab036
refactor: enforce required parameters in OIDC service methods
elibosley Aug 30, 2025
6efd398
feat: introduce OidcAuthorizationService for enhanced authorization h…
elibosley Aug 30, 2025
07f157f
refactor: consolidate OIDC services and enhance functionality
elibosley Aug 30, 2025
9a285cf
refactor: reorganize OIDC module structure and enhance service functi…
elibosley Aug 30, 2025
9fe2335
refactor: remove ConfigFile and ConfigFilesResponse types from GraphQ…
elibosley Aug 30, 2025
0176b28
feat: enhance OIDC authorization rules validation and safety
elibosley Aug 30, 2025
628cfff
refactor: improve OIDC request handling by enhancing header processing
elibosley Aug 30, 2025
f6930cf
refactor: reorganize OIDC module structure and introduce OidcBaseModule
elibosley Aug 30, 2025
428bfcf
refactor: improve OIDC client configuration and redirect URI handling
elibosley Aug 30, 2025
6fdb730
test: add unit tests for OidcTokenExchangeService
elibosley Aug 30, 2025
7d82693
test: enhance unit tests for OidcStateService
elibosley Aug 30, 2025
3cf1816
Potential fix for code scanning alert no. 197: Unused variable, impor…
elibosley Aug 30, 2025
0e8999f
test: clean up OidcTokenExchangeService unit test
elibosley Aug 30, 2025
77105c5
test: improve OidcStateService unit tests
elibosley Sep 2, 2025
b10ac1b
test: enhance OidcService integration tests for malformed JSON handling
elibosley Sep 2, 2025
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
5 changes: 4 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,4 +157,7 @@ Enables GraphQL playground at `http://tower.local/graphql`

- We are using tailwind v4 we do not need a tailwind config anymore
- always search the internet for tailwind v4 documentation when making tailwind related style changes
- never run or restart the API server or web server. I will handle the lifecylce, simply wait and ask me to do this for you
- never run or restart the API server or web server. I will handle the lifecycle, simply wait and ask me to do this for you
- Never use the `any` type. Always prefer proper typing
- Avoid using casting whenever possible, prefer proper typing from the start
- **IMPORTANT:** cache-manager v7 expects TTL values in **milliseconds**, not seconds. Always use milliseconds when setting cache TTL (e.g., 600000 for 10 minutes, not 600)
3 changes: 2 additions & 1 deletion api/dev/configs/oidc.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@
],
"buttonText": "Login With Unraid.net"
}
]
],
"defaultAllowedOrigins": []
}
23 changes: 19 additions & 4 deletions api/generated-schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -1798,6 +1798,8 @@ type Server implements Node {
guid: String!
apikey: String!
name: String!

"""Whether this server is online or offline"""
status: ServerStatus!
wanip: String!
lanip: String!
Expand Down Expand Up @@ -1854,7 +1856,7 @@ type OidcProvider {
"""
OIDC issuer URL (e.g., https://accounts.google.com). Required for auto-discovery via /.well-known/openid-configuration
"""
issuer: String!
issuer: String

"""
OAuth2 authorization endpoint URL. If omitted, will be auto-discovered from issuer/.well-known/openid-configuration
Expand Down Expand Up @@ -1907,6 +1909,16 @@ enum AuthorizationRuleMode {
AND
}

type OidcConfiguration {
"""List of configured OIDC providers"""
providers: [OidcProvider!]!

"""
Default allowed redirect origins that apply to all OIDC providers (e.g., Tailscale domains)
"""
defaultAllowedOrigins: [String!]
}

type OidcSessionValidation {
valid: Boolean!
username: String
Expand Down Expand Up @@ -2307,8 +2319,6 @@ type Query {
getApiKeyCreationFormSchema: ApiKeyFormSettings!
config: Config!
flash: Flash!
logFiles: [LogFile!]!
logFile(path: String!, lines: Int, startLine: Int): LogFileContent!
me: UserAccount!

"""Get all notifications"""
Expand All @@ -2335,6 +2345,8 @@ type Query {
disk(id: PrefixedID!): Disk!
rclone: RCloneBackupSettings!
info: Info!
logFiles: [LogFile!]!
logFile(path: String!, lines: Int, startLine: Int): LogFileContent!
settings: Settings!
isSSOEnabled: Boolean!

Expand All @@ -2347,6 +2359,9 @@ type Query {
"""Get a specific OIDC provider by ID"""
oidcProvider(id: PrefixedID!): OidcProvider

"""Get the full OIDC configuration (admin only)"""
oidcConfiguration: OidcConfiguration!

"""Validate an OIDC session token (internal use for CLI validation)"""
validateOidcSession(token: String!): OidcSessionValidation!
metrics: Metrics!
Expand Down Expand Up @@ -2590,13 +2605,13 @@ input AccessUrlInput {
}

type Subscription {
logFile(path: String!): LogFileContent!
notificationAdded: Notification!
notificationsOverview: NotificationOverview!
ownerSubscription: Owner!
serversSubscription: Server!
parityHistorySubscription: ParityCheck!
arraySubscription: UnraidArray!
logFile(path: String!): LogFileContent!
systemMetricsCpu: CpuUtilization!
systemMetricsMemory: MemoryUtilization!
upsUpdates: UPSDevice!
Expand Down
1 change: 1 addition & 0 deletions api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@
"diff": "8.0.2",
"dockerode": "4.0.7",
"dotenv": "17.2.1",
"escape-html": "1.0.3",
"execa": "9.6.0",
"exit-hook": "4.0.0",
"fastify": "5.5.0",
Expand Down
16 changes: 16 additions & 0 deletions api/src/core/log.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,24 @@ const stream = SUPPRESS_LOGS
singleLine: true,
hideObject: false,
colorize: true,
colorizeObjects: true,
levelFirst: false,
ignore: 'hostname,pid',
destination: logDestination,
translateTime: 'HH:mm:ss',
customPrettifiers: {
time: (timestamp: string | object) => `[${timestamp}`,
level: (logLevel: string | object, key: string, log: any, extras: any) => {
// Use labelColorized which preserves the colors
const { labelColorized } = extras;
const context = log.context || log.logger || 'app';
return `${labelColorized} ${context}]`;
},
},
messageFormat: (log: any, messageKey: string) => {
const msg = log[messageKey] || log.msg || '';
return msg;
},
})
: logDestination;

Expand Down
5 changes: 3 additions & 2 deletions api/src/core/pubsub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ export const pubsub = new PubSub({ eventEmitter });

/**
* Create a pubsub subscription.
* @param channel The pubsub channel to subscribe to.
* @param channel The pubsub channel to subscribe to. Can be either a predefined GRAPHQL_PUBSUB_CHANNEL
* or a dynamic string for runtime-generated topics (e.g., log file paths like "LOG_FILE:/var/log/test.log")
*/
export const createSubscription = <T = any>(
channel: GRAPHQL_PUBSUB_CHANNEL
channel: GRAPHQL_PUBSUB_CHANNEL | string
): AsyncIterableIterator<T> => {
return pubsub.asyncIterableIterator<T>(channel);
};
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { CacheModule } from '@nestjs/cache-manager';
import { Test } from '@nestjs/testing';

import { describe, expect, it } from 'vitest';
Expand All @@ -9,7 +10,7 @@ describe('Module Dependencies Integration', () => {
let module;
try {
module = await Test.createTestingModule({
imports: [RestModule],
imports: [CacheModule.register({ isGlobal: true }), RestModule],
}).compile();

expect(module).toBeDefined();
Expand Down
9 changes: 9 additions & 0 deletions api/src/unraid-api/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,15 @@ import { UnraidFileModifierModule } from '@app/unraid-api/unraid-file-modifier/u
req: () => undefined,
res: () => undefined,
},
formatters: {
log: (obj) => {
// Map NestJS context to Pino context field for pino-pretty
if (obj.context && !obj.logger) {
return { ...obj, logger: obj.context };
}
return obj;
},
},
},
}),
AuthModule,
Expand Down
34 changes: 33 additions & 1 deletion api/src/unraid-api/cli/generated/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,20 @@ export enum ConfigErrorState {
WITHDRAWN = 'WITHDRAWN'
}

export type ConfigFile = {
__typename?: 'ConfigFile';
content: Scalars['String']['output'];
name: Scalars['String']['output'];
path: Scalars['String']['output'];
/** Human-readable file size (e.g., "1.5 KB", "2.3 MB") */
sizeReadable: Scalars['String']['output'];
};

export type ConfigFilesResponse = {
__typename?: 'ConfigFilesResponse';
files: Array<ConfigFile>;
};

export type Connect = Node & {
__typename?: 'Connect';
/** The status of dynamic remote access */
Expand Down Expand Up @@ -1432,6 +1446,14 @@ export type OidcAuthorizationRule = {
value: Array<Scalars['String']['output']>;
};

export type OidcConfiguration = {
__typename?: 'OidcConfiguration';
/** Default allowed redirect origins that apply to all OIDC providers (e.g., Tailscale domains) */
defaultAllowedOrigins?: Maybe<Array<Scalars['String']['output']>>;
/** List of configured OIDC providers */
providers: Array<OidcProvider>;
};

export type OidcProvider = {
__typename?: 'OidcProvider';
/** OAuth2 authorization endpoint URL. If omitted, will be auto-discovered from issuer/.well-known/openid-configuration */
Expand All @@ -1455,7 +1477,7 @@ export type OidcProvider = {
/** The unique identifier for the OIDC provider */
id: Scalars['PrefixedID']['output'];
/** OIDC issuer URL (e.g., https://accounts.google.com). Required for auto-discovery via /.well-known/openid-configuration */
issuer: Scalars['String']['output'];
issuer?: Maybe<Scalars['String']['output']>;
/** JSON Web Key Set URI for token validation. If omitted, will be auto-discovered from issuer/.well-known/openid-configuration */
jwksUri?: Maybe<Scalars['String']['output']>;
/** Display name of the OIDC provider */
Expand Down Expand Up @@ -1623,6 +1645,7 @@ export type PublicPartnerInfo = {

export type Query = {
__typename?: 'Query';
allConfigFiles: ConfigFilesResponse;
apiKey?: Maybe<ApiKey>;
/** All possible permissions for API keys */
apiKeyPossiblePermissions: Array<Permission>;
Expand All @@ -1632,6 +1655,7 @@ export type Query = {
array: UnraidArray;
cloud: Cloud;
config: Config;
configFile?: Maybe<ConfigFile>;
connect: Connect;
customization?: Maybe<Customization>;
disk: Disk;
Expand All @@ -1654,6 +1678,8 @@ export type Query = {
network: Network;
/** Get all notifications */
notifications: Notifications;
/** Get the full OIDC configuration (admin only) */
oidcConfiguration: OidcConfiguration;
/** Get a specific OIDC provider by ID */
oidcProvider?: Maybe<OidcProvider>;
/** Get all configured OIDC providers (admin only) */
Expand Down Expand Up @@ -1693,6 +1719,11 @@ export type QueryApiKeyArgs = {
};


export type QueryConfigFileArgs = {
name: Scalars['String']['input'];
};


export type QueryDiskArgs = {
id: Scalars['PrefixedID']['input'];
};
Expand Down Expand Up @@ -1933,6 +1964,7 @@ export type Server = Node & {
name: Scalars['String']['output'];
owner: ProfileModel;
remoteurl: Scalars['String']['output'];
/** Whether this server is online or offline */
status: ServerStatus;
wanip: Scalars['String']['output'];
};
Expand Down
Loading
Loading