From 979a267bc5e128a8b789f0123e23c61860ebb11b Mon Sep 17 00:00:00 2001 From: Eli Bosley Date: Fri, 15 Aug 2025 11:59:21 -0400 Subject: [PATCH] feat: implement OIDC provider management in GraphQL API (#1563) Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .gitignore | 3 + api/.env.development | 1 + api/.gitignore | 5 + api/dev/configs/README.md | 34 + api/dev/configs/api.json | 14 +- api/dev/configs/connect.json | 4 +- api/dev/configs/oidc.json | 21 + api/docs/public/images/advanced-rules.png | Bin 0 -> 103268 bytes .../public/images/button-customization.png | Bin 0 -> 97812 bytes .../public/images/configured-provider.png | Bin 0 -> 86795 bytes .../public/images/default-unraid-provider.png | Bin 0 -> 131176 bytes api/docs/public/images/sso-with-options.png | Bin 0 -> 76436 bytes api/docs/public/index.md | 1 + api/docs/public/oidc-provider-setup.md | 402 +++++ api/generated-schema.graphql | 127 ++ api/package.json | 3 +- .../resolvers/rclone-api.service.test.ts | 67 +- api/src/__test__/setup.ts | 1 + api/src/__test__/setup/api-json-backup.ts | 36 + api/src/unraid-api/app/app.module.ts | 16 +- api/src/unraid-api/cli/generated/gql.ts | 6 + api/src/unraid-api/cli/generated/graphql.ts | 153 +- .../queries/validate-oidc-session.query.ts | 10 + .../cli/sso/validate-token.command.ts | 97 +- api/src/unraid-api/graph/graph.module.ts | 8 +- .../graph/introspection-plugin.spec.ts | 271 +++ .../unraid-api/graph/introspection-plugin.ts | 43 + .../resolvers/rclone/rclone-api.service.ts | 13 +- .../graph/resolvers/resolvers.module.ts | 2 + .../resolvers/settings/settings.model.ts | 12 + .../resolvers/settings/settings.module.ts | 20 +- .../resolvers/settings/settings.resolver.ts | 29 +- .../resolvers/settings/settings.service.ts | 55 +- .../resolvers/settings/sso-settings.model.ts | 10 + .../resolvers/sso/oidc-auth.service.test.ts | 1542 +++++++++++++++++ .../graph/resolvers/sso/oidc-auth.service.ts | 667 +++++++ .../resolvers/sso/oidc-config.service.ts | 1098 ++++++++++++ .../resolvers/sso/oidc-provider.model.ts | 168 ++ .../sso/oidc-session-validation.model.ts | 10 + .../sso/oidc-session.service.spec.ts | 121 ++ .../resolvers/sso/oidc-session.service.ts | 101 ++ .../resolvers/sso/oidc-state.service.spec.ts | 204 +++ .../graph/resolvers/sso/oidc-state.service.ts | 201 +++ .../resolvers/sso/oidc-validation.service.ts | 164 ++ .../sso/public-oidc-provider.model.ts | 22 + .../graph/resolvers/sso/sso-settings.types.ts | 7 + .../graph/resolvers/sso/sso.module.ts | 33 + .../graph/resolvers/sso/sso.resolver.ts | 107 ++ api/src/unraid-api/graph/sandbox-plugin.ts | 58 +- api/src/unraid-api/graph/utils/form-utils.ts | 82 + api/src/unraid-api/main.ts | 34 + api/src/unraid-api/rest/rest.controller.ts | 118 +- api/src/unraid-api/rest/rest.module.ts | 3 +- .../src/__test__/cloud.service.test.ts | 2 +- packages/unraid-shared/package.json | 1 + .../src/services/user-settings.ts | 25 +- pnpm-lock.yaml | 27 +- unraid-ui/package.json | 4 +- unraid-ui/src/forms/AccordionLayout.vue | 157 ++ unraid-ui/src/forms/HorizontalLayout.vue | 4 +- unraid-ui/src/forms/ObjectArrayField.vue | 251 +++ unraid-ui/src/forms/SteppedLayout.vue | 13 +- unraid-ui/src/forms/UnraidSettingsLayout.vue | 4 +- unraid-ui/src/forms/VerticalLayout.vue | 4 +- unraid-ui/src/forms/config.ts | 42 + unraid-ui/src/forms/renderers.ts | 40 +- unraid-ui/src/index.ts | 1 + web/__test__/components/SsoButton.test.ts | 351 ++-- .../ConnectSettings/ConnectSettings.ce.vue | 21 +- web/components/RClone/RCloneConfig.vue | 3 +- web/components/SsoButton.ce.vue | 181 +- .../queries/oidc-providers.query.ts | 28 + .../queries/public-oidc-providers.query.ts | 14 + web/components/sso/SsoButtons.vue | 45 + web/components/sso/SsoProviderButton.vue | 126 ++ web/components/sso/useSsoAuth.ts | 146 ++ web/components/sso/useSsoProviders.ts | 77 + web/composables/gql/gql.ts | 12 + web/composables/gql/graphql.ts | 322 ++++ web/nuxt.config.ts | 16 +- web/package.json | 1 + web/pages/login.vue | 165 +- web/server/api/debug/validate-token.post.ts | 84 + 83 files changed, 7852 insertions(+), 519 deletions(-) create mode 100644 api/dev/configs/README.md create mode 100644 api/dev/configs/oidc.json create mode 100644 api/docs/public/images/advanced-rules.png create mode 100644 api/docs/public/images/button-customization.png create mode 100644 api/docs/public/images/configured-provider.png create mode 100644 api/docs/public/images/default-unraid-provider.png create mode 100644 api/docs/public/images/sso-with-options.png create mode 100644 api/docs/public/oidc-provider-setup.md create mode 100644 api/src/__test__/setup/api-json-backup.ts create mode 100644 api/src/unraid-api/cli/queries/validate-oidc-session.query.ts create mode 100644 api/src/unraid-api/graph/introspection-plugin.spec.ts create mode 100644 api/src/unraid-api/graph/introspection-plugin.ts create mode 100644 api/src/unraid-api/graph/resolvers/settings/sso-settings.model.ts create mode 100644 api/src/unraid-api/graph/resolvers/sso/oidc-auth.service.test.ts create mode 100644 api/src/unraid-api/graph/resolvers/sso/oidc-auth.service.ts create mode 100644 api/src/unraid-api/graph/resolvers/sso/oidc-config.service.ts create mode 100644 api/src/unraid-api/graph/resolvers/sso/oidc-provider.model.ts create mode 100644 api/src/unraid-api/graph/resolvers/sso/oidc-session-validation.model.ts create mode 100644 api/src/unraid-api/graph/resolvers/sso/oidc-session.service.spec.ts create mode 100644 api/src/unraid-api/graph/resolvers/sso/oidc-session.service.ts create mode 100644 api/src/unraid-api/graph/resolvers/sso/oidc-state.service.spec.ts create mode 100644 api/src/unraid-api/graph/resolvers/sso/oidc-state.service.ts create mode 100644 api/src/unraid-api/graph/resolvers/sso/oidc-validation.service.ts create mode 100644 api/src/unraid-api/graph/resolvers/sso/public-oidc-provider.model.ts create mode 100644 api/src/unraid-api/graph/resolvers/sso/sso-settings.types.ts create mode 100644 api/src/unraid-api/graph/resolvers/sso/sso.module.ts create mode 100644 api/src/unraid-api/graph/resolvers/sso/sso.resolver.ts create mode 100644 unraid-ui/src/forms/AccordionLayout.vue create mode 100644 unraid-ui/src/forms/ObjectArrayField.vue create mode 100644 unraid-ui/src/forms/config.ts create mode 100644 web/components/queries/oidc-providers.query.ts create mode 100644 web/components/queries/public-oidc-providers.query.ts create mode 100644 web/components/sso/SsoButtons.vue create mode 100644 web/components/sso/SsoProviderButton.vue create mode 100644 web/components/sso/useSsoAuth.ts create mode 100644 web/components/sso/useSsoProviders.ts create mode 100644 web/server/api/debug/validate-token.post.ts diff --git a/.gitignore b/.gitignore index bcd82a863..4a1ba47fe 100644 --- a/.gitignore +++ b/.gitignore @@ -76,6 +76,9 @@ typescript # Github actions RELEASE_NOTES.md +# Test backups +api/dev/configs/api.json.backup + # Docker Deploy Folder deploy/* !deploy/.gitkeep diff --git a/api/.env.development b/api/.env.development index 22b9f174d..511bc0e67 100644 --- a/api/.env.development +++ b/api/.env.development @@ -17,6 +17,7 @@ PATHS_RCLONE_SOCKET=./dev/rclone-socket PATHS_LOG_BASE=./dev/log # Where we store logs PATHS_LOGS_FILE=./dev/log/graphql-api.log PATHS_CONNECT_STATUS_FILE_PATH=./dev/connectStatus.json # Connect plugin status file +PATHS_OIDC_JSON=./dev/configs/oidc.local.json ENVIRONMENT="development" NODE_ENV="development" PORT="3001" diff --git a/api/.gitignore b/api/.gitignore index 69e5493bd..1e587b405 100644 --- a/api/.gitignore +++ b/api/.gitignore @@ -86,3 +86,8 @@ deploy/* # local api configs - don't need project-wide tracking dev/connectStatus.json dev/configs/* +# local status - doesn't need to be tracked +dev/connectStatus.json + +# local OIDC config for testing - contains secrets +dev/configs/oidc.local.json diff --git a/api/dev/configs/README.md b/api/dev/configs/README.md new file mode 100644 index 000000000..4773d356e --- /dev/null +++ b/api/dev/configs/README.md @@ -0,0 +1,34 @@ +# Development Configuration Files + +This directory contains configuration files for local development. + +## OIDC Configuration + +### oidc.json +The default OIDC configuration file. This file is committed to git and should only contain non-sensitive test configurations. + +### Using a Local Configuration (gitignored) +For local testing with real OAuth providers: + +1. Create an `oidc.local.json` file based on `oidc.json` +2. Set the environment variable: `PATHS_OIDC_JSON=./dev/configs/oidc.local.json` +3. The API will load your local configuration instead of the default + +Example: +```bash +PATHS_OIDC_JSON=./dev/configs/oidc.local.json pnpm dev +``` + +### Setting up OAuth Apps + +#### Google +1. Go to [Google Cloud Console](https://console.cloud.google.com/) +2. Create a new project or select existing +3. Enable Google+ API +4. Create OAuth 2.0 credentials +5. Add authorized redirect URI: `http://localhost:3000/graphql/api/auth/oidc/callback` + +#### GitHub +1. Go to GitHub Settings > Developer settings > OAuth Apps +2. Create a new OAuth App +3. Set Authorization callback URL: `http://localhost:3000/graphql/api/auth/oidc/callback` \ No newline at end of file diff --git a/api/dev/configs/api.json b/api/dev/configs/api.json index 812a29f73..51fda8706 100644 --- a/api/dev/configs/api.json +++ b/api/dev/configs/api.json @@ -1,7 +1,9 @@ { - "version": "4.12.0", - "extraOrigins": [], - "sandbox": true, - "ssoSubIds": [], - "plugins": ["unraid-api-plugin-connect"] -} + "version": "4.12.0", + "extraOrigins": [], + "sandbox": true, + "ssoSubIds": [], + "plugins": [ + "unraid-api-plugin-connect" + ] +} \ No newline at end of file diff --git a/api/dev/configs/connect.json b/api/dev/configs/connect.json index aed9f170e..a853ba3f9 100644 --- a/api/dev/configs/connect.json +++ b/api/dev/configs/connect.json @@ -2,11 +2,11 @@ "wanaccess": true, "wanport": 8443, "upnpEnabled": false, - "apikey": "_______________________BIG_API_KEY_HERE_________________________", + "apikey": "", "localApiKey": "_______________________LOCAL_API_KEY_HERE_________________________", "email": "test@example.com", "username": "zspearmint", "avatar": "https://via.placeholder.com/200", "regWizTime": "1611175408732_0951-1653-3509-FBA155FA23C0", - "dynamicRemoteAccessType": "DISABLED" + "dynamicRemoteAccessType": "STATIC" } \ No newline at end of file diff --git a/api/dev/configs/oidc.json b/api/dev/configs/oidc.json new file mode 100644 index 000000000..c9317ecee --- /dev/null +++ b/api/dev/configs/oidc.json @@ -0,0 +1,21 @@ +{ + "providers": [ + { + "id": "unraid.net", + "name": "Unraid.net", + "clientId": "CONNECT_SERVER_SSO", + "issuer": "https://account.unraid.net", + "authorizationEndpoint": "https://account.unraid.net/sso/", + "tokenEndpoint": "https://account.unraid.net/api/oauth2/token", + "scopes": [ + "openid", + "profile", + "email" + ], + "authorizedSubIds": [ + "297294e2-b31c-4bcc-a441-88aee0ad609f" + ], + "buttonText": "Login With Unraid.net" + } + ] +} \ No newline at end of file diff --git a/api/docs/public/images/advanced-rules.png b/api/docs/public/images/advanced-rules.png new file mode 100644 index 0000000000000000000000000000000000000000..dd60e6dc0d62443c8e07980846f7762929219f87 GIT binary patch literal 103268 zcmeFZWmuHo+Bb}Vqyi2g3epT+A|c(|2L#}D~jT4adtMQq0(BTAxY z1~L;{1yBjmlB4*A%xda95`Pwz$M~!0tDof~W2UdP&!4w16BsKg&iwSF&1fcli|n%E zcAUz^eU*MZDClyu=y9|$h>Z|9{46-yfE(d`!0y@y7A|*V6?S4SWL6>cU%n`c>z^jX z1~bS6Klhg0Lp+;MGw8e)9ON-V;WWwR)_a}Tj`K(iH_w)QG2|INVF3V_TE z#JZ1+bd|C5QzYX^+^*&JuX*IAmlE%b%3}j7ANf`@{*r?!IQVt(TE#b-m+WZD%U~Id zutlsNF@(ju`o%S1hp12(L6%C)GjPWq5vln+A4O&+47d7w_29FSO)@Ern0ef5EAzX- zfYGm-!#Dw`k`6K$r>SQKQiq086^<;PnF$MvO=;r}l2S06W5hZ~8+Va{`tw(B9d6D7 z2OGbeM~yGKdb&h#r?VEe(Jsj~#nW<2?u{s1M$b>MiR3s;L=A9raY?#=34WEHp^4)C z19McE7EL0Jyf@;ppD#Yb?%mX`ap`7&)D@}Occ@cA-O$n(xA-`c!t}gxUk>|`BKgpqV-#Vm?jxaf=Q6RB*G8fW{H&kR zjD0B-h3|z$05hm#;Fqf_I0fCbllK>>flvU0tAk*b=tQ@H!24-L20}sd4mIAH<7Bj}Aki2ZUv2Zh6#}`SVdck@T!x9OZ*x5v5 zhdT>90_Uu^L|^=rI=_E-#cCx>MNLwT5#vwS{i-XaTc>;R=aUZt29+9w(+HM97v1j+ za4RhBoKgRlZBnkirhP zGC)J2S=LkJPKY9^M(Q%>C)Fc!%EAE06vwzpiCYmhGNKsGja*}NH`E)P#<;u4J71B^ z9c3N5u~^4`ENP}q(B{j{>L;q*vR{}wW9G<|nI~u+WVT8fh1C?|=^#3s%5@r-IR|oc zT=vM^Ufl7oBy3uA8W&$S!t5!bDsD12LN|Cfo;Q@xnGIC@PZ+ltBp7^wR)K_pG8k|b zJk_B7Ec-(30@Q+pLZlE4aU}6N!z{zJwbwtHe;R-v=LSlzN*BlV#|_h6j4QHx5hlmW zGGwG?EM%p6~&)&stGeGFi;vC|LVvp$~3P)A>RH9X!3)u_#RK<%4 zi<#AQbDQ)ps`ip^Le7Y{9k#cJ1z$fz5te)`d0tYdR{E|~*;-6jbV)`xN3Y0R3Lb_c z*QrXWlr6d5+#N}+I-#kYp_$_*e1@+ZJe)%_A+vLMZotIBWXlw+7N?dl-B9Q2{QdId zAvb?f3n|PblBT9Zh5>Xam+j702<$_jU?|^$XI5k`t$pooR zI8QjW9al6YGjvtD)1s62od)F>rRXt%Y#XE5+3vUP4U5-#=vIc76s&WmPa#@G;e8dn!8;Ri3z+5gb@f-WX@kNSk;dekzYe!svo|-w7DcY7@7C6t8kKZdAoX&kX>jLz5cAHT^uF!Iuwg>&t=?s?X4>J z17~nk|BDrAf0Pcu5`-R`p6_U!(Mh)`jIt z=ZYEoaRh4wTZC!aeA;Q6Q(C7Ur=DetR?F+<)@4ZZarM-kjx&?TY|pT1sse;%j;pJF z@TxJrGJUrEC+X$Y>B`{)l@N>2i%~o@#5rFGQaZ{@)D}aFs;q#Tbz}Nh34#U93ecSrIoO0Wpae ztGjV+5H)B@C(G`leV6OEU7- zQ8<4eSUcKm-D-q}xQcqxc#}Vvq$ukcEPiNKzB{_kL{11;3*bVG&yYP~9^=Z1bzTI#0=BwY|mxgTQx`TqYSyd370S44OA)pFAneAy0=wVi{;DW^_g^*8oOPG>Nr^5@W1iV3)LIYU5L+(tEDTpC3euA zayRojxUGB2LsZ4}#Oi$B)fLxHc|320Q`?Gp@y<@BD?Z-p;vj4eF6atZOrVUR9Byoz zva{Tb*dcu^=xklRtk!g>&CGJ;o+hqmw=5>W^>FJva0p(Yap^kTp|4)j0DlW-4!=gr!-3Sb#?qp`wHCb$nJ!CM!uz6Vi%DN z@?Kv=o+uu#{^~reb*`aQWfs+EP7!mAyKoCptlZ8Wn8cXYG8Zq?Mo_FoW)DO$C{ZlS(pZ*8U)9I7L!Zw|U zy;tF{NL*>Wxo!j={QHAMNeR5Ih|me$F6lNWuFZ~2<3@toSX&*xiGOQ(rO)A;OnYN- zUb~rmTu&227ZXDf{{77oMn>eLo}Z6S?r{)eX%PyR5O5L(&u1nOhdgm*ajzBlCj1pb znGiTi5dv(Ge_)U0ew~*1Qo?|sY^_wl@VT@!*Qkyl?Pwui4C%(ktR=(|Yjoln>4`$( z?rz%vES*p?`(Ew=bDIGZ>BIDZ8GvHnnrKLUl#@fC2d>c&9tD{rAOlyAfFDBO2LS;w z1M%OlActfi{rehe@b^tA)!lOh1R(?|F<}+gM+d3s4XP8co)z}u97!<@^Y?|4()~Dz z(VDS^iUpB$g32HJ@sy~fbAwSt3!ae2;^7A>W{EhM%RgM`E_vOsVwEX!A`FB?ay+ggqx7>A!e&V7)HXRMTtqc;(iIZd9eG6^l!s_my&D|yid0^@u} zKqfcwlQbS@1g{-06)J)ke5@NXP|ZBEu` zeIri`%4oTqAHcw;H@0urYV&TfUZ`WLZyY22<0*mn_i`eRbLQAd?niEs9~!A^MCp#1 zJD=03mKyg(*V(PV({8i}8!9;q_LF+~*YQS>p(Z<;-YCPf)zC;%-hRF9`lRunBOt@kz?jUqk-?Z&-2Y-VZjmGA8Z1+3qkR>$9z)Iw0oX zbVP{%5;U^NZ8SW(_gx_bEH+@7_!`5XA%|6`o7$OXLrENU<`dKmsztGJEc(mHc>aGT z76Rf;)aO?pavVy41eA`YH82^7Ujk3`CS;m2sFhR@^Ezw%48bpT+#UZa2HBX-RQ}5o zRE7Tjg#TIdR@{&cDV2s#1`7@&{4S4KZUg={F1NH$QqcdGW74$m3UvJL7-=A`37Ie{B_n+%zf!NqYgXn&o}4TXgNv$nbtX z>NNk$a*;(^AmfC*xBi*P^0&nPDvFRvi}gZY^narNpOeT}g%a2>77W9lf1S)LAPjat z?q((i5?Ja9^gYKuj-V{4cTKMaPpe!Qpj2sHh)DCUHx%ny={t_DGaDl^5)?HbUUGSF7G8 zb=UG?;IZhJCzTwQ(;9{EbMIx|?3U>^e9F}I7~*iAkmH(rI#n#uZ=Y2zx&9g}++uOI zh}izCc~~AdxeauqkJG682@$VNifXYMQ#XE!-=C}bs|_S+9+6evdfOk*sA>3=ki8;| zh-*HG)cacBZENoWh*R5CnJ%yT?ch)=d*;~)?+D4^)PQ^AXcHZ&j}_O7XLa9#`9^P4 zBbz>t?;oF^L*X0yJY?B({V9V^L&b&9y&HpWQ_Zf&Y?|j9x~1D%aGf{&ws+TQfBMG3 zE6u&1({}kFB3~i>h+ki&equ^pVh;rs4!fv+K9sDzo*iDb;Bj72s$GvFe(w;AD>z?o zY3j5;S&2C;xPsDrBGLr4(S^*5TQAHf=zE)+raQ%#=VL%`xsMkeD4iBk-10~xx}SM3 zInC%s@d!TLRXgpBwuhU|j<)gW*agCG_sd&#;LcK*?L>U86}CoulZCB}O)RN{I}O`I z$)?sU&7&4GMJD;F?nPdwKM7ig2AgXwSC*X@thtgo9P-+%fWyq&6Q1V5tovp}09GQ{ zX203*xZSD0k?D9?JCh_b-=%Eb>p)j=xKIyURG>-wTAhOmC3*Xusjb5Uo#x&V|phS(S;-`PsV;iG@(+Dr@4vLH#AD9ezH5tCL;L3y?Fp%yRveBv;< zyseH?o@^{d&?PG@Ao6!X5k@7ox=3}2D*@0)Ygo(G@`6vq5kvL5K!&}^%H?9C!{JA+ zmBFO$GL8k)bg#SIg*xjq2iVz&-~`8o#C(#}L9hv^lo@jV9yT48U5dNH-WEjSPD^aN z8NUY`d^265`3AB%tzD{^DWazXJE$HpNPT8GhO5P#i_RRQ zB*tL!j24G{4s3?vA$XOL15u*8Jw#^#^kHgL6 zJ-Y?#%fShS1#mdb8+K?4tYq1g%ksM`o_6^>*0<5d9V0 z6c;%F5*>yWoK!zFT|?)iB&n2P&=2oB%;xG>ye*;MxJLq$AL|K%D4CnC4fRQuH;Ww; z+4QbYAHQ^sxvret)Zg277RLlRs`H*J?vLk&5(MAh?z6z!53TA1tb1x!beFzqee5n3 zTj+T^lJa9Cmoi2%q1XNG*nUQlDtw=c=0nTbFzHqx`Tcmdq+dJh zHaUd{w2y|;vN%N)O06VMvB}tv^&2)!a3}*Hl~g1*24rFEtGL=0(2Barqu(7S!R^jw z2=#us&-XFS4pK?H=8T0Yd*;0nC1EwVU#{)gtENapncm*O#&*PiIZL_JFpzgTdxTsi zHVw^*@np_BWL}hA7Es0J={;}aL0!rd^8zIL!k5_&<>d1b>dZ!^`OW`7mX@hC1x zy1a!!<%B!l#vZ+PQ~b-HNCJc2H`a3xes?y))N6oCC4t3qB9_o_o#HSYoHAcy9usFr z!q2t|?PFG+O_03_?|AN6?)l@<>cmT)_@F_4=E;4-?KsEy0a3iV`(cP=aBaCgdi{t) z8aGBYC{zQzp>sx@bi1n|8>Yx+vuPDgcRX5L-fDqMEqk4y9yd*G6+CUa;&pYQ3Y=tC z9;2J!5KcGGz6~XIi*vI!$}}jbg~#gqg;Q3tJ=M~Y$jsj|4IKHJAP$ie_Q9nZTky7b z`Qev;ujOS5w}7ydA~U%-=UH(%1+pQ!43tgDh8(Oj{B6pvG1xeUMo!vA^Q6V5#eH{} z29`xU$K$fM(em{O6Fi7j2D9jXa{MeiMZDH>rVcn>=d2Q_c&(&InI_cEPZ=RS#O3L~ zJnQ$%uQJ^t8g-<7f8?LM4%{D-U3kn?0IMCS#T${1XWZ>P+G>gv3|yDGn~>p7$dgSp z{0%C8*FE4oJ#J}m&ny+qB`u30QN6!L@e2dF)!X*U zTS$ksLixrp*YYQSI)(8kG+VTe2Q?G&)kz$dD5`JH3zxc{Gie#mK{rGA(WC7H~)?0+26t~v2r0E!1mad2-V5yYk5Qnxh|D;zQ(#hNq9dC0@H zxgOC%MkQdGqL^nqEq%*`{U#7xCn?iQZ2k@D&olQCWu(2x7=W29iq%T1E^UH}HS(55 z^5j!3j(IXS5$pHAJEME-<|Z7pdK_P5YGFfO2`1<}Yc|P%vE+=j<;{jyyyaAzKMKsZsDa1;TX824*sch+;Bk~_*;Ioi74bJo6 zjt)iPXQ4PDP#AIl^HEp;m zyAmKAnu%;CRoR6N*2e0j*%A?#UXFW{ef@C^rT{!VHH;EV`5lZR^>Q0vlsZ7GnBC}$ zISk`kt_a4Zxvlsuu+1Goj*rSoxolE~zQ69Q15A$JOl@}}A7)9fUS?%ET~aCFb$k7( zdUGHF#)hsecz>P!l!R|KoM(^rHyyHGYMSbfAhiVsYWmG6)LK2=jw`Djci;1#kBV9Y z;hb*s@!{Qw6s7Rw=*|d>V^G&xY4x&>;J;*bV<{}fm48dl<{WnFk1bFE5QT~Q%*J=g zBCC@hmztcs#(q;E(IBkaPAuL*py-)naCxi+(=-!AHD?IiUmF!`R_EEOucz_4lo5aW z@yPp$1=-8`oqJXNOgBOp5N1>8Wsz2G(XqA@0Z@WEzdSwLKbcb3cl$`6unU;gjKH81b%x_3Q(Zd zG}o5vwa`50i{wG8WGJZXA1Bs^DdJpiAeAA`5hd6GZ`a_>oe>n*pc>*Ua_zxzaa5 zwwqT`8DyBq6#TD9z0Su3Udb=ZSNV}B){yw&&1-r1N|H*Mm(fS zk-o)5P7!b30_?@jKh)*G~~gKGW@ZiYdPH| zu(L^Jh1%aFEA&&k_ibT$`a#-zaIg{(lqg%7tFnMzvk9KT#5X;|d}p$}`z6%}K(bfP z=y?pOPr!?B%)C^XAE`J@wJ>2$%UzXv>ud1_XYQ$6;(VV^AOj22F<7G;r;#56j z{8x+XEh<`dR=X`!Tm9HMSf^M{d+%jFbT*0bP!^Weg{kw*Zr(o=7=$eE^E$$b79}m90 z89t#1mS@zg>Y9m~1zCe$HMtyr6Qnpq#^)frOAQ z`Xy~*9> z03rgbnIHM4dR@+Wm$kdN@k`N;d(li(9&s!gXa+C=R;$vEfF!D#DxaSR@LW2HX)(CoAJbg=XBf@rZ(jBpA`RPg~4S_9GJ-!?;uX zEIubyr8xDf5VdWQS*BSV{PJvBQTm|t^q_9RW;#n8lQb>Y67Lp&E5iZf5JPq?Kz5nc z-6TN!Q)xGSCGDV);qYdHK8ebyNQiTIOeDBn)KH%_J>$JAABKO-wj$%iZQ;{kMy>o0 zrGA%GY#czbr3jZyX#&m?FJ*dQTa@N+kwifFO!2y@%PG+9KqTO*owV3p9US+3;DGQr zYRqQ|^;S+qCU-kEv!=q6Qj9VW17AzkV9GjCN%t4iW#@U&^BgdFmfptvD5ww%)&kA@ zc?hS8g2M2Uo_`-Uzc;`PeaBPoCgq4{x&*=Knc&t`TR129lP15U0PuQLGZ9s{WX4xc zH;Dpc@!;PY@tq;^lVmJ&_Gs%KOdv77bxAk0WkUjJ(&~gl&6)+oS?&nzK}IpOOkJdXP#@Mvf8$@B~+OYD&B)4h_r3BW@sIN=U#?5n23`P^|9`JLJq&%8vd+E-9urSZ=)@IElNf=F zov=~Yl+_lV+-~Fk6U9d3N^)mPN}m5JT%^GWL=VcKORTYfvIGc!0y*#e{7KZ1Yzf`6 zc6@cPJLe4CXA@3xA_)Ui`lvu9`fUQ^p1-UqjxjcqqWT6XqUc^pV*i4kpouI~Vf2R5 zUrAi0t`xn%@5VbP_m!6F;P3`}#=G=A zeL;%sAiH-MnDru`Pna-YEK1Bsu3J|m=bB+q zo@H}=cL`EYpc$Ji`)`a*%gRa z1lw{f^;8WmD&PARaMhN3h&2YXJA;GO+xn%bY^mUCu?q}1{#55?Ot^^wRJAmZWR0-8 zSaf5iIB_o|{_xXsXd;)1sl7CP;dxX)=dGoszFlOLDiQ}j#>iGe3nOU)|Dt7-grw1# zf2uf(k0wRU8uociHG&U*6#q7dk6U`aWRcIH{3W_(5zRA+RTaWekz|l2tFv3qj<2@g zXOYkDmj}BsxIIm3%RHdrNeLX3JcWy5);u$XNDb>9ol%!}je+|zcp%e5$-D~sK>1zp zm{=MoVR|$v4JOeyF3wbS5j!`!J`ZUTvA68fn-U~$ISO~B_fGm>GrL4J0o1a2+F*&r z?|y#L5rfR_Rx8C9{h{Df(*yk2XL&%f2c${0Q^1%eB@{iI1LI^Y*l8AfX&1Yb3lrZj zXzZ)9SNKWjFEUK^(Oj2(YpPh?!{fH&CQ8))w&u=m{?rjYg;x6_CkxtIa*`=O%LxEq5Z`a{-! zRkSGWxY?wyJuzMLyG%(CVG^hHUW`1d-V2`m*XAWG%_n{YSg{D2BJU_H7wSr}E61_) zKw2A4SxYsO>Ws>7)AqiKvI=hYDVeGK+Qr>((%5DjU@BmVzzb6~I+(4@zG_7eAPOci z%<5V(V5!i6)9~c${y=FYp1-|3+~>EVBD_DJP>7O=O_y}@+~~{p6oz&v(Z=za8V7e5 zLB$rq2=qEKhQY{q!Qt~tvT4dtmG=uEuZ^F%PLXCWsc6*~#;E0?Y|3UIG!&yGqC}>t zG%j{0dPao%ayZmA)s72y?pd04D2TZ2a|pO=IiXGO+p0c10SRoHV$yTY>tBZp|P%Qs(WK%3Tkr?UXAB1rO z#W!VCB&&xQz@z*j9YW`yh%U9z5*`C*pS=jxdl*zNsy?#WsI)sHmQ8x zE1CHYfi!TFZA=)YUh>$sKc5!x>-ePs@#N5sseDc1SZg8b|IXbEGF(ZwOfz+sGLg{O zX7z=X0n2y6pYh#emO6UN(rDnOERVo}YVR-AO0_gq>qUwGlyrNs04h8ud@5L%nKLGg zxce}RmynP4XcIPu`KM27Oyhfvu2oe{F#i{~3$Pt~&j5oGdIx$f^iOp0Ti%Q01@77h zy=nQ6E%{%Q@#Rwh?8_0BXGniFog06fn=WrBF#p14|L?H>rRDr9$W@7F9>=Ygza5oi zPV4^Z_y@o?fD~A3d(70PD+84+eJ+qd=Yt^pTv^^Jis4WmFm=n@zdR+f&lUWO(TInK zNXLXXl*&8v+tu|MO6Fpju;e*yxw86!$uj#}PMc}HnAV<#xY!QZL;-ya_?(v0jIh%n zQVK74fB!9LhEZtM z9wCU5e{cjjf^OrfIq!lKW=)mzZ>y$FQw*>L&lPjhu)MV@ehhPOzbe+w4|h749RuP= zK$oC6zFxObGWWViBZgY;U@O_S4iGjZ`c?B<^P4Ue?2Pt5{ls66*#X4Wnny?|*X{~@ zrvc^J?yCtRE5=ZJ_wy);67jPwHHaD$@Ag6U;Q&k9xk-fJeFH$D9PY1&d5>%A2=sO$ zBqB(x_b3bUW1x{u*IA*mdO{d7L~lNRD=)5p))@qA-K2B;xtXBte|vEluWi$(xHJDt zT`#yM8_Dgh*RfcU*+dd;?D20E{WgfyMqSq}_sSh(W`CtTJ$14w*OYr_Goe#FY8_ym z6A^8COHIxK`)9m(qw+@hISU|NrH^wmQQ$yGuU|?MN z0hLfJnbS5rMSE>;Kcy=apvQB+4Z49Op7#7aT%!)>Vq6a_o*gm<{nlnWu~O5Kv{ns7 z`5-J}s|H-na6YIci%s+Wyro|AY=d2P`59nAz`UDQT(jF+>u}B4u`XID

=TL8t|d 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 I(gxoD3n!4utN;K2 literal 0 HcmV?d00001 diff --git a/api/docs/public/images/button-customization.png b/api/docs/public/images/button-customization.png new file mode 100644 index 0000000000000000000000000000000000000000..516cd0c3cd9d703315129c918a2dea2e13392439 GIT binary patch literal 97812 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 Q*BOAo)78&qol`;+04(}33;+NC literal 0 HcmV?d00001 diff --git a/api/docs/public/images/default-unraid-provider.png b/api/docs/public/images/default-unraid-provider.png new file mode 100644 index 0000000000000000000000000000000000000000..8d1f3227d6d04960a730ec48fa864ccd5243f78e GIT binary patch literal 131176 zcmeEuWmr^O*fuSK2udkP7<7t+^w3Jf&@g~>cc*}ev`Tk(4^o4GsDN}g0|?U0&|Tl= zsOLTB;Lq>RdtL7j=9+8PX73fxdg8wCwSyJqrSNdba8OWC@SZ;tS4Kg>$OHb~U}FKl zw0ab$qM+bZnv01kJ{J?CR&=m6F}E^CL3tJ&6L(D|YW()gjf*@UEowAnQQHAbv}e~d z0$C}nUOfWQ-^cO~8Pn83mALgTmwC4EjlU(T5z8BT5|ZX=QX?hB(H4LD^p8|eFwKAvA#^Yh6!b!n{!4A4U>xZQ{Z#O93 zl};`E@>}tFy%+3SI)nG56XQrz(?!{X-uXOx)R+DQ5eqr?U;?4Qu*ap(l5e7{J~bHC zjA2cj@f*&#&J^Vt+3<3>({+`+C7*Qp`ykM98*~&>)LD4S7o|y;yuC z969eRBMmij!9zECOZauV`AzP9(|sxB!m{YV3RJ&J=2^KD1xNp{{8q7b=EcjJ@-jDH z^m0VbBbmaZ?#=Ra+M_8HL{g_v@O7SYM!wY~$-|Nv4JWLOuk5s8+y%^reR*Pc1b;B2Mrym*c=H<(-3SDa{-?Y;Q4zj0}UYM|v%RQ*C9 z)6%wU6HXA(4{MIqD%XB!=}B0a^d_^A)UCt3K{G@4jO9zz)#8Zn)8KBFKq8spH^Lm&V!K(mz0%B!9&ijJjFrfZ--ah$( zdL+`UNJNC*A}0C_%~(V`T_B%;F`zJAYngQUhSMz=)*c8yL&yx<13$c(KK=&N=hbo{ ztD8^sY1eUsK6lQ#xzQwFH~qptd%KoQ2#4o0>38x?NG+)kUSv8-$g9kZ4~n_;SRsKp z;u2YNiUva-Dy)TfRKp9Us?zxsYm^%DdGguJm=i;Cv*h|C%xsqhoS#bw}U zk@q8(CzpkG*^kLz`zw8k*T2VZCHsh$vJxjMfT8u?*W^~6)~S{o`a&-%G{{C!EQ9u1 zC0`I$SXLa3d5>XPF;!oijHGD(ZpZkPFqx2*pshJ)-oY-XHmer5R=XB;%2eQbS#Zr~ z&$+BE(Nhzz$LHAR0_P;>G(O4HQhg7f5};id_)I2>`W+=B&@PG{bce2k5burrS4D#d z_JkF$G!#C{dWoKb9=!YTd_TM85vuvaf>%z-PBBAL$D(S~L;494Ax=jk9O*~Db2EQd*SXv~*F})ZZg5&#a$8!BweHzR?H$ROC8m1DofyX$^5_l5$bvpq0hRYEE(M$g z0;&>4WJRoMkQ|ua9&|P7C}f9X$#H3^NBHr@JF;SvVv^!&wGzn^u(dcu3@!u7)-CjT zein`;_eJ%gQr5HikF9TMRR=Y}>6+Q@B0I#8;GS%{L78R5?h6(!7F(8JwHURyk=klM zm-u}X1QsHXPn_ble6svI`J|rm(%jM}n^>C^J!N-;JO+cMpt>HJ1NpI7H}c*LH;LnC(CoDHdm#t(f1l+|&@LRw>`D zn6er-IGXMl3$awSac)aAK$tf)!^V%{qEUgTfhVLwvyBaFw}chwP(_igsc*Lx>??aQ5?xjy~A8JAUH_Bi~$j~kW4<2o)Zo?~r2rYQ>b zY~ws%Yq}2V(kjx%%37%Q54L6y7mq+}pgVm;*l6Q^_2_9>cQHzC96LXDvG1GPZejCh z38?hv7tO!VBpUI`3&$Ck1>5_c$sOA3mbh_vdStrfLS)}5mG1PDn^V@YZc9d6L{k^? zl95tSdb4{N1-HEyl);LfVD8rRuuW)&4tJKas6$deo=)M66DJeX8L~gV^;pV)_Nia8 zMb9&4{^|!V@tt$Xg~r9Y?GQIHFFK$57skn8oflL3AHk={!wihLS97m;&|=eNH(C4n zD>&>q8Q5182aFuV9mH^iXlNJ06h-16St$%ZgQQG6RKFL=m&WR`=)mh*-MXu}*q#VJ@zo90?SxFm=EPJn z6xmWZLWVueyw{Jb@%YH0JU6U%C*0f!?ZE@NGu+x%tW&4tz!W4F~uAEkbA+%Z94m?sNbnU0bg?KKGT{;molXR|M5zCC_EMrZC z_2Al_Y1TMM5bgr*6WkNmlMNf@Owt7s@2N_I-m1C4mKe`qPa028nN#UR8D|;SSovt0 zXgG^2lWo3Y(U{s}SY*w{h|lQRUd(EYdyJv*bsrDK1=V4V5Hz_pppCnMfBZN8u42TP?j|kmWc2n-MY* zO~*8gah`Nu3LjF8l8c3NTnpzo_nd4V4%Oz`*D_=j)bJM8Ers~P~ecbg~dikixEPzX&c5G*=}FCV` zMtZ08ilqu8m-d^^A0i@y{nkmNNn6;s^hR!K;iXb+-&^&m`Z4^;vVU!hhu62`LUU_m z6H(BlbFq3a;vS_Noe$5E&_zH;kQf!Ij}`fKGWUIkg~3BJq-jiVP!oHjQUQ7D2Ax02NCg4;5g}TQQKNjH_r)BqUllc;V1-gUAv=$Xx(0fvV?~U z0)qhxp)4p|B`B|KFh1e;=e!w_dR@$f0=8DlXL?yul4Dp+nu?sv6GuO?F>45My4g2) zi)vFLVP&PM^CXSzS=PDS1@00PF1r3mrx}pNJ~h^OZXzd#!U%lFMnMfSN5KHTp#pzo zz#j?z`ZYWq0XlLkuzuYBK#p0^uw;$^+%)7dwIui@vVO6UoQPq@7(; z0tep6E~NJ&?^0}Zy@7=CdkPEaN&+-GI{J|g=lJd`+$XnCF$h2?|N5X8^?PrCBVXV> zh?zOijlc8^_6ZC-dciO7;3WzgHub+gT9_4`ni;rUa#8aOQ}1Bp7BaSZ{O1yEYDFuw zy%5Ap=Z{r)1q!>ZNe#8_yRKd|hL#iP?baU2it)~ZF_h7Y?T9iV;K~EBsR`H=KQtGh z-13)J#ZwO4`?BvQ{_p<(HJooc>B|9NKSrT%(aajeO*$HBQdb5FLhb}YY7&3wcz!BMbHVCS1^0ia8n_XN3joE@M8PNgT!@cugBZ zlWz0jgCoX$=_1pflZfg{A@AdCNGj%kO%hlNSxhKpvNBe$rfI5qmQ-YQATFs|{((hN ziOEumZ=-cRqMA5YcXHEn7Mo|>vwRcy-pA{E<^96Cipi#f=goKxh!MD-RJT6_u~!MM>50+ zZDdg6!SX`9B=HvCp((N2HoQ&6(7FhIld2LCw%H=ZCv0T)hxSs~ot*=-1R?C+r zrH7zBIhb}-2()fEnD*RHBa~+kZ^=f!!WTG9mplAH=(z-UwO${qs94V=x2v zrq5byOY?zl_#QSRDTJn6#*ImvCf1N{LDLR0v`P6@#y5F)6>XtI$}+8Dvrv5_O+#}qSI#;w#kD#KZrBunU!Cy8bfS)RcK2IW zq=|#9j~89|PB!%WYx3hXxXTQD-wD1wBq-Nh1ozzAo)O zEud@^{Lb6Wh27lnp-3980nF0sag!u=enb{*w?KQ2Hp$9>ytt{6>dh&lW~Q1@fv-LC zB-`c{$n4myn%YY#o~2Y#mYpTlP)eJI&};Z3%#%&FdcwJ}zTeNm zu1%!6Zq23%?PabT)7A44{IAC^JdzhC7KTT4pA0KFPupdMdyPsv>xIS2ur&;s$fhBe zN=8eP1lbKi(Jtv{I&bX-tX+uB=QTg_X4f^%iwiAq5u z{^0xZ4<~)F#{2 z5qW4VNJhV+PGz~N-TJ{STk9C=$pFta3U zLE#~-Rj`%ZBm>Afk%}_!B&Hp*>B8qi;Phbg3;xMg8l|&ev8oQW)Y@x=8NrcH(7oFv zRa>m-TvNt21?#J=lLIf8UyRG?yp=(3EXv0ww6UWzK+&Bn!{MtM*quR&p z$Fo5XSGc!p4%P?!lOI<@>9*Kxno17y5SP=SEK4w#dLIC)<6rMR%-F?gdskac>U~WTqcV60dCa`eBskRTc3p(_484i z*EjAOr0~1$H&f%Gl~IqWszZozWzE?SZs7~euBBI$J4t;)*D-m=(adS*^=;qdXd#Zt zivQz?{^HhMxfdds#SsE?*i`Y#x$j|Tcy;F8ab#`!#7Fm?Q#bVh$o_~$QK^ON4e~{Fa0m8at=?(~ z@9v0piAVqzcaQVdy#+KCqANj{UdON4SpIAUnyy0h_PHv|Y2|j3U^}KkAvoGOM?Zwa zC7L&fiBUA^R;!o#(`+zeIVkDenSERS8)nz?u>J~0COkE zdepOqG+mt6H|!3`_~`EdQDltHCb_s~+N1N@OEMmt^-;X$0^L$_Lnx~Q6gxpCFWI9o zWwv!jdE9fO!P?xE`>Qd(MCYiUFXAA}9OqHsyppzj3g57e;~=FIoQn8I2uZm8-q$Dv zt)QC}22pKI7mdD+jceOu&T!|m9XJF_oo+Nm89{`|yrUgVcO~L0vZGy4T`!myE3C=Z z)>&?hwK~9!bFv*g6^C5BFNw1{{v3#VR!l3Gc-V&ZPG8YUuYIJ~>G*{Wfh%VHBAbT( zMxEa*uvh9cCJ}TICX)ZFNF{&^>Z?*|5Gvod0To;Nj@BQHsx@>1b z(E_k1D71}R!|tBfRTng`$DopQkZ4;e-Dl5PS*v?FM8^usM@EE33Qa_E<=&{-8_{Mf zZhd&wIh0>IhmKSw{fA_Cp8vR)qBp#i``IO?juVd8RRTr6XHIc`DDt+f@wfwjf+Du8 zXP%AKE;~(;Q*xNkCf(VXIFSF^Yj+Q;H5gn%=FT;lK{aRi%v@+|qOu($GhJ^t&&-r5 zXTuz9pW!JnG*?Z>zGZAod26zNjo_0qsSN!7W36cO5vZ{Yt_WTC;HQO*8~50$1xhya z)3Z2=MP0?Z!-OT}$T`j47Fg!A7Ae;Vo#hu-AFnoQXTb24uOuGnc9>9-WaU0lcpuSj zU&{3D@Gp<{+2y04A(@cV!huH{A9v)fstBS7Kr5>@`v)_=CpS{Z(Cih4yyaaxnVb#p zr>rC2#Myv$QEe1Ax#FA%RX$0LgK)Nuv^c-H)062jS~1BcL5=R^8$akQuuOyP1*S)@ z7DMyu*K`XrQKCa8L=%k3HW-M)ZxK^Gf(-JDW92jf9{zLYd4*N~115+=kf5<^4ik)N z_FE9;wn)cPs`tpWEBurw|9C&icKtIh>n7rX?H@dN?&Tt_z9vDExrX2h3H-q7jBv3e ztbutnY@Y7u>IS&CneM6NK5ujiC`g-!I;oIxTbZrFX5&NrWmiSHO}g%QcEQhV^jzD7 zjHx9;(Kz$cToS3!3ru@c&C$VH>aI|2!*H(5q=F$wxU&Lf2WTK(arHK7h`$7^K2^HL z2L9Q|m#4834%y};iSZXOY>U|2InKT=ADEuDpR>n3X;nGKJxpW&4)>{qj8hu9E%bTS z1dWyYylUW98&TL={l$5%Y;}K@)QP^nM#R9QJeK&)2#|K%?No4S#&aefq@L*&K1hZ*^-H+CB#$#+>`D-t zed4sN2yT_9U#h>6LV~RFXK$+uql4d<*A#~_r68ZxM3Udp32-dBw~S0wCzUYdvM&FD z^yjJNUozLBB@Tx;x85~9l)@az^y8!mfbA3{`%b(2bkSkf4N0)7Dv@Yn-(Zwk+36Lo z+1_?&>IK75R2+=+s2m0h>#x}}N2($4R>tY*ZyCkfZjwP#!BCo(-< z2Py6ntKdq&aVOdCa(V~efR=ssxLT=!&aWG>I7+LK>bPYv$ViSq?sz|-aG4Hc{p%0vpE2DPi_(fYaSpz z)iy;`m4u!gwd2i|+J7~2&Ye`lb_?8_qgp8Pn{`fxJ;~r`7;%|A>M*Wj7?g7*71;cQ zKCeE{b=57s1&wgz8Rge+KzzU8xkT5?B%x6<-oKN*w5sbeb9SI^SBQJB|6AfRcK z-n&W{`POZ5NY(%;R7OlGmFT@J7(EC&qGdS_BzQJ?wX&?3x~qlEm0cYBpV09SKdJEZ z=)joD0@XkbB)-SUgN zd7C?R^ST;7MJ3hSJ40~gyC$pWd7q-uyj`c<^Y9A;jlqJ9Z=Bm{;to*~S`$3xw%QQn z2l=c%_!0Na`8S|4g~JI!SBgA$R>e7(ZOqc=$6aZ{Gp;S*xJHuhq)8m)O-C3yggc~Z zaJsDN*spn*MINH_Cj|!8mT%^?l*{qtjf=Nzl`!VEj~$$z>uEg)yijWh zv5h}^ZsF-{nf-4J;Ic6Nj{8H_u5hz}SM*B22WmGrzhalQ(6Sq4e<(jNLS%p{+P;cM zD)h=Quzy4Y#c;C4O~yX}0Yq^?+nj}M1j*I%`x^tQKbUv;o(k60HhNZ|tz~%n9?KOI zp?(Vns&Vs%f)4_6X{mmvA; z1j!u*i|HJHXtM{}7)$pT&92UxU-E70r!nAe5#uWX3&n2^Xj9ceboc)Oui>Qu*)irN z26S9mLKL(XpslcWA}Q++u!Ms&ut@S;*J4DjLKgqGfdAhYkiL8Wdru-)=VDKarLnQG znyzjtVzu1&55}d2AuTOk{Nck;BIjS|Oj(Tgk9kQM9S}X!t^1f+xpGM+xTLIIQar0+ zSGSjnW;&3okHDNQsfr7?C2@>eik;UUva3>?5(N2OxjcqM5gK9ekf^d4BX1}sGK@i9 z_)3T9O;B4Ti5@cSzmJ)XZjI0UlbNxC!oD&yp=VltWcZzzWRrdy=zF`ur1nY1EM58c zuhfd_XkVW}iIV5=5iq`%as10o2@=9tJ}~<78n48BFn1(h=^<8o@fXP8Wdl zxv~kOFBv_%Kc)WKuGI1%^nA-?Mq{s0mysCgx4jlf6s?@aUMJWeY#-hw^Ya1F`ve@h z*s{iA!bd{GlpRJ70~qUWIsV$!={6WdVTs(;ThsyoD4I@MP;|TgXCeX78el~;3n^5xdzZA@hrb=kI#D2ONKT+a7H!ztYV)qFp z>|b@0s2Ch1fDe64jQa;Si9PfI2rh{v#-3OGG78#V05tB155c_Zmr*e+v;apb7YhBg z5dV6vUl=B~ys>PH|L>m4&wjxd{tzY4*S^@D6z9}`>!IXWC|hH^lTSsr#z(gbZvY;= ztSL+v`kGFV{PjTMZ-tzy(;OG(|K+3oV7Cr;2NmRm>okA>WKg6?_j8d7>C5$-(JoU+ zi2Fj!#l+_X05(NT)?AG4CMIw8OHqv*MhcpMb7h~y#@9;gdn6<7B{=ywo65$sH`~-L zkVHwa#;pwIoq(njXJ3ai)TGnUpE6CvD~yVx&WI=kSQk8cvY))r0c<47ymrF%F2+sMs=N>2 zqiR~_H0131FTDYXyF#_2vl9W_>WT5a0$sXfJ5lDfC7|}M20GHgR=*IdeQX$75-+9@ z4ar41)y(+RTQ}^DTo$Km6IDeJ`xfGAU`8j$Dhv&)&Q5oF5w-IXm2y1rq5W}Fs(PTS zhC91PKt0ZF)q*Bo$7eOax5yLQ$eR~AuX-LyYp>9X4|H94%V2YJEYPueT6 z6Gq{++3b_nM&;{;d9myuAoV+DTZmp7Cp{GJmzPikHQ#cs0Nc$sU7VGj0ce`b*o&a? zOo`Cb{?qf5jr{=`wj=#FxlB(pOU(d)J&DVDV(3~95sI?@kF7SUgEcd49Cn76*$&4m zc_%2cWx5Ssl6|~O$%Yd)(9BXdEj?5g%*va>r*@v(?Q{rWc#$OzD>-q;pQL9Ry%Awg zdN$|P8Q$c2Bg?=-mR?YV)N6!mNA z!&*vlO%*(x4VV@+8P#zN_ug#2#_hiS1Lx!>+qBP#^+po_r%wV8v?E{tX29 zO%TwRgopq3hy>s8{a7Ke!>t6B%Nd88E}C^^A>s8W=o+n8w%{|0*@r&93(Y`qfj2Q!o&%lG+#LZ?I`l*A`N=VSx$ z8k^G@x?~&AdosSM`BfP?l23~N3+o!#ZIyS zWMv3Okx#H7tC8N3k(TyS(|&5xt=|99I99XOR54ILE4Qq6E@Y&_P)blv1ArVeiwhKq zsE79He>;4TH8v1!@KmdV|MEScSd3`R?9vwaLTx5>la6h)O*P|&1U09eJy;6yGK*VRX^X+ zKMy=%d-{6xyZzQ&%XvXh6u`Q@YH#dJZ@r!ME7=11VAB&?Z%2bdO{mz zO=U)4XQfi@YWH58?kLQ_tpGb7?UW`x%g8aD0^*3*CvoyVd&Vs3d3}k{)r2AmL&Jik zWUMzEEWu1=-F!#K4b!r0AI_g0-&Jp zW-G0qE-=%#$2(39HSQHW*mWfnKztZn2f&YQO%*;$_5Qy4a)g^+F4bBG~*dh-*X;P5{i=a%itQ-d9p$qga-iktZnsv+%fKs&6uQz^-Y1q z(eJYS>c#XKWEJ+@ie!|8kC(IL0&*?AF}0iy_}~zEi2!$|PnmuVRo0lk4kh$rD~%_} z9alb;P1dRVN^R-Kg-K~4(6q}i?(6!wVSl32RF;7~2ensq$XcNl#*`^w^syO>{Loo6 zHwvp{W2hhsUx$G`A0-}X6JKmbee^tvcDye`43Qbix_#GyY)BWC>wl^Nax@xN;>M5M z+{qS61vd9nECd&S((;OCXvbW|rS>?KLy|{8;PT#=#daAQ5)$PpJA7RH-deBIk~y25 zQrU^uxk-jo5GI?sv|9KSK+lEGRWpxXoIb+zx5xx&IJQQajMg`W?U`Nl#`FwKa)$0S zOdX0~ZY}7%5=eO`Gr@c!)?c42jmc>ZOsyC&v}W_>?Bqb9nqL?aPzqq0iLBlXG4a0~ z#vW#-p*ihD;vD@-?DG%0PFl;ydT{=oVR1<2-hd-V-kgNnPj()JdfjYxbAle=*2{)W zz$|((pQ2q#SuG3vdE5P;#uU0yFc&}0;B!SLyC?ufi)@`N6qEszFD#k?2;r3gZL&z& zyIMei@a#aC=c1YXrd;T6_^o2)Sb+V2jD8hr{WIhffT%ms=u({CTLX+5x6i4YZ=8_x z^`S%XcX=PpQp)XJDw%IrJ3&Q&9jU0EOiwMME(t2(bFV`n6wOxP3i3Hg*DFS_r!f2h9ru zR+R^VTIYW<^+IZP+v$&t#Vw4^G@z5A7|xSlP=}v58bD}P0KSYaQ8jN+<=SvgF77{z z0~Jw_dH-<&m!%rbh(SM$MDgTRb>W(K<=@qDy8F&zt}N2(ygT2!=t@zNpZ(IGQu-ZW zwoOcRw4tMcFDE1rSuVeWEehKBZ8JEmsvA&F{8i19Fy}`-&gX26-p}#2m?;#qj!xX5 z*C4MlO8V&-h-zP=QC+4n-iJxt)>^YtNy0%qrakv&SohXOd())B;9sUd{|q&J?*qV? zovat!ePVLRoI64J2;!S%(X`s3uIUVxSw1L{24K)ujgw+ter|aX)&4$tX3;+e?B)B zTO3^l=S{wXZ>34MtftCwb;ze@h&0CIMWuOqdTr?dZ^posPb!G|%4>hyEK@VXnIB}b zz?AbBXm>Y`$bG_3F4br*wUnRvHiDFpxn2Z_*=UZR`421Wd`usHk zMrik`Go}2&?a>yt9-Czig5}1)`!{=m`5W`VLJqKCUsohPS!vsPc&Qo6CZtw;FaPG~ zZ?Aoh!)ex+K8wXuCT)4@XqjGEtl91Cge%I0hWUrx!j0M@PDGwjn?a=G>bGqrx@;rf zkvZ_GOtVy)`|cRe&m`;jy@AI8s}G&yYK?|%7pUJ|pzGg^<@NylB?YK{3mqL?-u$)7|NbBVpia!|f5+#4-f>k2s6A4m zD#iZP8EOEPe75n4_A1R!07~xxD*ItxUj9Ex(XfFL_+v!=>5k5< z0OZ=!K7RkoIsy|-K#!*YJ*oxj_#}SU(ry6A2V5IelX(BCs0*8@0c37oz-2M??GgvX zBj+wwjePH*ZsCk?&MjovdLxO)HkHeAqzeG8toYo1?}fid2J{WIj}a}9?bf|MUg`ZY z*SIsbN-d!nD-VFR3;@4T1Eki&6R6M^e_FjlT-h`sI0=iEk@aMC$+4tzaRMMI;>eBu zAfeAFvBr-3PdoSp3ZSB5=3gS0+#LB(4r*9yyt2g|zk+*D>+{jB&c$0v(Bpe84n%_m zE0?$;mhk8u@b579caXYtQl|up%uum^Hv0b}?LVf}NN6g~gv!h%F0>|e;!zi;_A4OrjpN_hTN%86Q$0BFO>yvzHC zEl4d4*vx`yy^`hCIZda&$;2w>WbZ%lqr|0Cl5 znjybaA^`77Lr9if-SB??mx@G!?r$RhG>Kp1JYoQLr|@LB)?fbY-xpxs2Mm}x@m;__ zm*LlPZ-asT;!{mtCHW7(^Q#T#5{VGue0c3D%#8gb57>jJRy-Devh!C!o4=XlO|(DQ zlmg%mj>c_<$gZ~0V*_oN{bBbo{$O4Iw}Aia7cl2Y6VWm1(d>`O?TXY%683G3iHTA5 zK`;K3eZBmTuk7+8Q!T@hxRrb`D+V zT8KA2R;I=?p}(%(bPRti@dw@x2LgIb&?35BL1bko?Yc0SuKx$0n%;*=gs#N=h)Sjz zE05Db&~>4+4g8+(>O+nHaou*sa(>)1o0n;$K z=+_WDr&QG|#4(&Y8 zkCwnTK7fkXrJURV+ZbBm5q`>c1Bi zGh}P7cfmeW{PnD8=Ihx((m0ikZrgn{Ps9srrk8&5WDA$+d; zC*(XfI)TGODO-U2o13B&kknf)rMadSFr{@ME>6-Ye5dTsj|2~<=GXQC`sHXe;dB&0 zq9*|szfTshvzc=r%78*p=baNlvw{31igdvDn1f=r!J ztLN#N6S#E?coBH_p7ycjdEhlHkN`e7 zk|m*Vx+2i+zuOl?31m;w!)6PiaO!27k9k;RmHB@7@>~{*26%7<^aH~MyeT6d`eQca zO&_i5*v{nfN<>jvBh(V&(wAh|su5{j-S3T#&?T*P2Uw4@Y2v;*C%C8e!R#IWO$O>J02Dq# z8OAI1oT85ljxId~c#{48HmVbSMJb+hFpv5*ylkJswN%oM;)ExNKCzn>{R%=W=)y_X zumZtl7%`>eZhtQIWJgi)-00wT$Lk4?wUW!D2ru@+C?`Ld&&Lf`6o{`4m%k93d=W%( zx(kp>y{;50)v9`N)~bsUXAgK-hX4if!crLk`%W(cy5H@xy_u;xM{l)}OO7m@j0@@} zjCaR(EF);bku^TQ^gUCp;nN<>^u`Nh{TTPIIH~}z*P_=k>}6`i7Qh&VBeXjo71b;% z{dPRRv$1ccsIa{pi-x9|CnuiSnuBU4p`FFL=5@HTb<TU4##3wZ@#f$AFz8j?jcY@&1DrD8ua0bIoqvv67^AMs%Pl7o zEd!BMZSpBCElr+ z)HhcI;;vW4;4&5@yCUn(S)FlGOX?OAT{`*c@!f(b%a z#gTLo^t39cH;%CH$7|NUaYs4Dr9fddF4ObqumJ$ug$zp$Qaz6mUoTvDN zf>G9vP3Y(cAviXIk~r+T1nPV71pwqbh!RVG#7u}+`pP_*GEU2aVaA!g%d#$eIy83O z6Ns!nYP}Gb>!DvMn;h<{sxquT$VZ0Yurlz=pymzgxFqBT4rIFT;FQHQbL+?hKIq-g%DrFog9-tG5@bil!7HEG{` z|C=lQxP(2#@oMb~I}`cf#TmF}%&BXwQ4Y8av6Z!7dxSn4kJK75YR~4f>E2ErSW~W9 zFEfp=a=y!pcfOLikekbrqNdTzwgadczCvGrmKKJYGp58E5eBZPy6XVEC?J^h8R;=z zzTq5@Zh3eQh^##ekC%3Sq*CBqly+GVw+Hd{1CuUixp z4vyvG#-r(}qb(YUV-HuKX3=~^0y#=rF>4S7pHn$0ycWD_aj=x;n~0{?9&&nHQzDOn zP!3>B<7(}50ns@0e{j@agI4^8#+oU56!(cO4$!oV^Nbr4u_Pk=6_KVHCz_m{9tbs) z1{dddj0aN+g90pBM1>BdQ-|)GP26!7pL1;p>|);8SY?~#Mne-Q-GAH?W@Db$Q`u?) zQlSCY?`u_Ul+~e~D#~^ecV%a4fjJL>;TFDX9@*0gczMY&duU}Wm-@7Glru8lajz)F zeQPe8dl!lHV$o6OKLZZQjZM4F;2;!=FLqb#IKB3HDv~yJ}ndMksDQEekWhJ)D?0P!C_hLavDq6h#V;Ebq)s`fKJ9icWkqjgp8;;yPgcfQS;yMC11_-aysT3s!n5JDBDK z-;34rabH_$^@lyaL z>q6QId)9*Vob-3bL}gOVk|O!p;NF|;2Os^&YbzA6ZoeZEo_;uD$#;vFFHa3Bov#oF-S*mvi1Oq*n84v$tI4)N+-m6Qg!U_L` zszyc#TXc+L(OOR)(wnZnemVuYp9Om3uh1^-$ip3lrm7C%qr`5tEZZ#)8
+Advanced Mode + +Advanced mode provides granular control using claim-based rules. You can: + +- Create complex authorization rules based on JWT claims +- Use operators like equals, contains, endsWith, startsWith +- Combine multiple conditions with OR/AND logic +- Choose whether ANY rule must pass (OR mode) or ALL rules must pass (AND mode) + +**When to use Advanced Mode:** + +- You need to check group memberships +- You want to verify multiple claims (e.g., email domain AND verified status) +- You have complex authorization requirements +- You need fine-grained control over how rules are evaluated + +
+ +## Authorization Rules + +![Authorization Rules Configuration](./images/advanced-rules.png) +_Screenshot: Advanced authorization rules showing JWT claim configuration with email endsWith operator for domain-based access control_ + +### Simple Mode Examples + +#### Allow Company Domain + +In Simple Authorization: + +- **Allowed Email Domains**: Enter `company.com` +- This allows anyone with @company.com email + +#### Allow Specific Users + +- **Specific Email Addresses**: Add individual emails +- Click **Add Item** to add multiple addresses + +
+Advanced Mode Examples + +#### Authorization Rule Mode + +When using multiple rules, you can choose how they're evaluated: + +- **OR Mode** (default): User is authorized if ANY rule passes +- **AND Mode**: User is authorized only if ALL rules pass + +#### Email Domain with Verification (AND Mode) + +To require both email domain AND verification: + +1. Set **Authorization Rule Mode** to `AND` +2. Add two rules: + - Rule 1: + - **Claim**: `email` + - **Operator**: `endsWith` + - **Value**: `@company.com` + - Rule 2: + - **Claim**: `email_verified` + - **Operator**: `equals` + - **Value**: `true` + +This ensures users must have both a company email AND a verified email address. + +#### Group-Based Access (OR Mode) + +To allow access to multiple groups: + +1. Set **Authorization Rule Mode** to `OR` (default) +2. Add rules for each group: + - **Claim**: `groups` + - **Operator**: `contains` + - **Value**: `admins` + + Or add another rule: + - **Claim**: `groups` + - **Operator**: `contains` + - **Value**: `developers` + +Users in either `admins` OR `developers` group will be authorized. + +#### Multiple Domains + +- **Claim**: `email` +- **Operator**: `endsWith` +- **Values**: Add multiple domains (e.g., `company.com`, `subsidiary.com`) + +#### Complex Authorization (AND Mode) + +For strict security requiring multiple conditions: + +1. Set **Authorization Rule Mode** to `AND` +2. Add multiple rules that ALL must pass: + - Email must be from company domain + - Email must be verified + - User must be in specific group + - Account must have 2FA enabled (if claim available) + +
+ +
+Configuration Interface Details + +### Provider Tabs + +- Each configured provider appears as a tab at the top +- Click a tab to switch between provider configurations +- The **+** button on the right adds a new provider + +### Authorization Mode Dropdown + +- **simple**: Best for email-based authorization (recommended for most users) +- **advanced**: For complex claim-based rules using JWT claims + +### Simple Authorization Fields + +When "simple" mode is selected, you'll see: + +- **Allowed Email Domains**: Enter domains without @ (e.g., `company.com`) + - Helper text: "Users with emails ending in these domains can login" +- **Specific Email Addresses**: Add individual email addresses + - Helper text: "Only these exact email addresses can login" +- **Add Item** buttons to add multiple entries + +### Advanced Authorization Fields + +When "advanced" mode is selected, you'll see: + +- **Authorization Rule Mode**: Choose `OR` (any rule passes) or `AND` (all rules must pass) +- **Authorization Rules**: Add multiple claim-based rules +- **For each rule**: + - **Claim**: The JWT claim to check + - **Operator**: How to compare (equals, contains, endsWith, startsWith) + - **Value**: What to match against + +### Additional Interface Elements + +- **Enable Developer Sandbox**: Toggle to enable GraphQL sandbox at `/graphql` +- The interface uses a dark theme for better visibility +- Field validation indicators help ensure correct configuration + +
+ +### Required Redirect URI + +All providers must be configured with this redirect URI: + +``` +http://YOUR_UNRAID_IP:3001/graphql/api/auth/oidc/callback +``` + +Replace `YOUR_UNRAID_IP` with your actual server IP address. + +### Issuer URL Format + +The **Issuer URL** field accepts both formats, but **base URL is strongly recommended** for security: + +- **Base URL** (recommended): `https://accounts.google.com` +- **Full discovery URL**: `https://accounts.google.com/.well-known/openid-configuration` + +**⚠️ Security Note**: Always use the base URL format when possible. The system automatically appends `/.well-known/openid-configuration` for OIDC discovery. Using the full discovery URL directly disables important issuer validation checks and is not recommended by the OpenID Connect specification. + +**Examples of correct base URLs:** +- Google: `https://accounts.google.com` +- Microsoft/Azure: `https://login.microsoftonline.com/YOUR_TENANT_ID/v2.0` +- Keycloak: `https://keycloak.example.com/realms/YOUR_REALM` +- Authelia: `https://auth.yourdomain.com` + +## Testing Your Configuration + +![Login Page with SSO Buttons](./images/sso-with-options.png) +_Screenshot: Unraid login page displaying both traditional username/password authentication and SSO options with customized provider buttons_ + +1. Save your provider configuration +2. Log out (if logged in) +3. Navigate to the login page +4. Your configured provider button should appear +5. Click to test the login flow + +## Troubleshooting + +### Common Issues + +#### "Provider not found" error + +- Ensure the Issuer URL is correct +- Check that the provider supports OIDC discovery (/.well-known/openid-configuration) + +#### "Authorization failed" + +- In Simple Mode: Check email domains are entered correctly (without @) +- In Advanced Mode: + - Verify claim names match exactly what your provider sends + - Check if Authorization Rule Mode is set correctly (OR vs AND) + - Ensure all required claims are present in the token +- Enable debug logging to see actual claims and rule evaluation + +#### "Invalid redirect URI" + +- Ensure the redirect URI in your provider matches exactly +- Include the port number (:3001) +- Use HTTP for local, HTTPS for production + +#### Cannot see login button + +- Check that at least one authorization rule is configured +- Verify the provider is enabled/saved + +### Debug Mode + +To troubleshoot issues: + +1. Enable debug logging: + +```bash +LOG_LEVEL=debug unraid-api start --debug +``` + +2. Check logs for: + +- Received claims from provider +- Authorization rule evaluation +- Token validation errors + +## Security Best Practices + +1. **Always use HTTPS in production** - OAuth requires secure connections +2. **Use Simple Mode for authorization** - Prevents overly accepting configurations and reduces misconfiguration risks +3. **Be specific with authorization** - Don't use overly broad rules +4. **Rotate secrets regularly** - Update client secrets periodically +5. **Test thoroughly** - Verify only intended users can access + +## Need Help? + +- Check provider's OIDC documentation +- Review Unraid API logs for detailed error messages +- Ensure your provider supports standard OIDC discovery +- Verify network connectivity between Unraid and provider + +## Provider-Specific Setup + +### Unraid.net Provider + +The Unraid.net provider is built-in and pre-configured. You only need to configure authorization rules in the interface. + +**Configuration:** +- **Issuer URL**: Pre-configured (built-in provider) +- **Client ID/Secret**: Pre-configured (built-in provider) +- **Redirect URI**: `http://YOUR_UNRAID_IP:3001/graphql/api/auth/oidc/callback` + +:::warning[Security Notice] +**Always use HTTPS for production redirect URIs!** The examples above use HTTP for initial setup and testing only. In production environments, you MUST use HTTPS (e.g., `https://YOUR_UNRAID_IP:3001/graphql/api/auth/oidc/callback`) to ensure secure communication and prevent credential interception. Most OIDC providers will reject HTTP redirect URIs for security reasons. +::: + +Configure authorization rules using Simple Mode (allowed email domains/addresses) or Advanced Mode for complex requirements. + +### Google + +Set up OAuth 2.0 credentials in [Google Cloud Console](https://console.cloud.google.com/): + +1. Go to **APIs & Services** → **Credentials** +2. Click **Create Credentials** → **OAuth client ID** +3. Choose **Web application** as the application type +4. Add your redirect URI to **Authorized redirect URIs** +5. Configure the OAuth consent screen if prompted + +**Configuration:** + +- **Issuer URL**: `https://accounts.google.com` +- **Client ID/Secret**: From your OAuth 2.0 client credentials +- **Required Scopes**: `openid`, `profile`, `email` +- **Redirect URI**: `http://YOUR_UNRAID_IP:3001/graphql/api/auth/oidc/callback` + +:::warning[Google Domain Requirements] +**Google requires valid domain names for OAuth redirect URIs.** Local IP addresses and `.local` domains are not accepted. To use Google OAuth with your Unraid server, you'll need: + +- **Option 1: Reverse Proxy** - Set up a reverse proxy (like NGINX Proxy Manager or Traefik) with a valid domain name pointing to your Unraid API +- **Option 2: Tailscale** - Use Tailscale to get a valid `*.ts.net` domain that Google will accept +- **Option 3: Dynamic DNS** - Use a DDNS service to get a public domain name for your server + +Remember to update your redirect URI in both Google Cloud Console and your Unraid OIDC configuration to use the valid domain. +::: + +For Google Workspace domains, use Advanced Mode with the `hd` claim to restrict access to your organization's domain. + +### Authelia + +Configure OIDC client in your Authelia `configuration.yml` with client ID `unraid-api` and generate a hashed secret using the Authelia hash-password command. + +**Configuration:** + +- **Issuer URL**: `https://auth.yourdomain.com` +- **Client ID**: `unraid-api` (or as configured in Authelia) +- **Client Secret**: Your unhashed secret +- **Required Scopes**: `openid`, `profile`, `email`, `groups` +- **Redirect URI**: `http://YOUR_UNRAID_IP:3001/graphql/api/auth/oidc/callback` + +Use Advanced Mode with `groups` claim for group-based authorization. + +### Microsoft/Azure AD + +Register a new app in [Azure Portal](https://portal.azure.com/) under Azure Active Directory → App registrations. Note the Application ID, create a client secret, and note your tenant ID. + +**Configuration:** + +- **Issuer URL**: `https://login.microsoftonline.com/YOUR_TENANT_ID/v2.0` +- **Client ID**: Your Application (client) ID +- **Client Secret**: Generated client secret +- **Required Scopes**: `openid`, `profile`, `email` +- **Redirect URI**: `http://YOUR_UNRAID_IP:3001/graphql/api/auth/oidc/callback` + +Authorization rules can be configured in the interface using email domains or advanced claims. + +### Keycloak + +Create a new confidential client in Keycloak Admin Console with `openid-connect` protocol and copy the client secret from the Credentials tab. + +**Configuration:** + +- **Issuer URL**: `https://keycloak.example.com/realms/YOUR_REALM` +- **Client ID**: `unraid-api` (or as configured in Keycloak) +- **Client Secret**: From Keycloak Credentials tab +- **Required Scopes**: `openid`, `profile`, `email` +- **Redirect URI**: `http://YOUR_UNRAID_IP:3001/graphql/api/auth/oidc/callback` + +For role-based authorization, use Advanced Mode with `realm_access.roles` or `resource_access` claims. + +### Authentik + +Create a new OAuth2/OpenID Provider in Authentik, then create an Application and link it to the provider. + +**Configuration:** + +- **Issuer URL**: `https://authentik.example.com/application/o/unraid-api/` +- **Client ID**: From Authentik provider configuration +- **Client Secret**: From Authentik provider configuration +- **Required Scopes**: `openid`, `profile`, `email` +- **Redirect URI**: `http://YOUR_UNRAID_IP:3001/graphql/api/auth/oidc/callback` + +Authorization rules can be configured in the interface. + +### Okta + +Create a new OIDC Web Application in Okta Admin Console and assign appropriate users or groups. + +**Configuration:** + +- **Issuer URL**: `https://YOUR_DOMAIN.okta.com` +- **Client ID**: From Okta application configuration +- **Client Secret**: From Okta application configuration +- **Required Scopes**: `openid`, `profile`, `email` +- **Redirect URI**: `http://YOUR_UNRAID_IP:3001/graphql/api/auth/oidc/callback` + +Authorization rules can be configured in the interface using email domains or advanced claims. diff --git a/api/generated-schema.graphql b/api/generated-schema.graphql index 8a096ce51..85c4b5dad 100644 --- a/api/generated-schema.graphql +++ b/api/generated-schema.graphql @@ -1400,6 +1400,13 @@ type ApiConfig { plugins: [String!]! } +type SsoSettings implements Node { + id: PrefixedID! + + """List of configured OIDC providers""" + oidcProviders: [OidcProvider!]! +} + type UnifiedSettings implements Node { id: PrefixedID! @@ -1419,6 +1426,9 @@ type UpdateSettingsResponse { """The updated settings values""" values: JSON! + + """Warning messages about configuration issues found during validation""" + warnings: [String!] } type Settings implements Node { @@ -1427,10 +1437,115 @@ type Settings implements Node { """A view of all settings""" unified: UnifiedSettings! + """SSO settings""" + sso: SsoSettings! + """The API setting values""" api: ApiConfig! } +type OidcAuthorizationRule { + """The claim to check (e.g., email, sub, groups, hd)""" + claim: String! + + """The comparison operator""" + operator: AuthorizationOperator! + + """The value(s) to match against""" + value: [String!]! +} + +"""Operators for authorization rule matching""" +enum AuthorizationOperator { + EQUALS + CONTAINS + ENDS_WITH + STARTS_WITH +} + +type OidcProvider { + """The unique identifier for the OIDC provider""" + id: PrefixedID! + + """Display name of the OIDC provider""" + name: String! + + """OAuth2 client ID registered with the provider""" + clientId: String! + + """OAuth2 client secret (if required by provider)""" + clientSecret: String + + """ + OIDC issuer URL (e.g., https://accounts.google.com). Required for auto-discovery via /.well-known/openid-configuration + """ + issuer: String! + + """ + OAuth2 authorization endpoint URL. If omitted, will be auto-discovered from issuer/.well-known/openid-configuration + """ + authorizationEndpoint: String + + """ + OAuth2 token endpoint URL. If omitted, will be auto-discovered from issuer/.well-known/openid-configuration + """ + tokenEndpoint: String + + """ + JSON Web Key Set URI for token validation. If omitted, will be auto-discovered from issuer/.well-known/openid-configuration + """ + jwksUri: String + + """OAuth2 scopes to request (e.g., openid, profile, email)""" + scopes: [String!]! + + """Flexible authorization rules based on claims""" + authorizationRules: [OidcAuthorizationRule!] + + """ + Mode for evaluating authorization rules - OR (any rule passes) or AND (all rules must pass). Defaults to OR. + """ + authorizationRuleMode: AuthorizationRuleMode + + """Custom text for the login button""" + buttonText: String + + """URL or base64 encoded icon for the login button""" + buttonIcon: String + + """ + Button variant style from Reka UI. See https://reka-ui.com/docs/components/button + """ + buttonVariant: String + + """ + Custom CSS styles for the button (e.g., "background: linear-gradient(to right, #4f46e5, #7c3aed); border-radius: 9999px;") + """ + buttonStyle: String +} + +""" +Mode for evaluating authorization rules - OR (any rule passes) or AND (all rules must pass) +""" +enum AuthorizationRuleMode { + OR + AND +} + +type OidcSessionValidation { + valid: Boolean! + username: String +} + +type PublicOidcProvider { + id: ID! + name: String! + buttonText: String + buttonIcon: String + buttonVariant: String + buttonStyle: String +} + type UPSBattery { """ Battery charge level as a percentage (0-100). Unit: percent (%). Example: 100 means battery is fully charged @@ -1834,6 +1949,18 @@ type Query { rclone: RCloneBackupSettings! settings: Settings! isSSOEnabled: Boolean! + + """Get public OIDC provider information for login buttons""" + publicOidcProviders: [PublicOidcProvider!]! + + """Get all configured OIDC providers (admin only)""" + oidcProviders: [OidcProvider!]! + + """Get a specific OIDC provider by ID""" + oidcProvider(id: PrefixedID!): OidcProvider + + """Validate an OIDC session token (internal use for CLI validation)""" + validateOidcSession(token: String!): OidcSessionValidation! upsDevices: [UPSDevice!]! upsDeviceById(id: String!): UPSDevice upsConfiguration: UPSConfiguration! diff --git a/api/package.json b/api/package.json index 49110e6c9..8415bf995 100644 --- a/api/package.json +++ b/api/package.json @@ -15,7 +15,7 @@ "scripts": { "// Development": "", "start": "node dist/main.js", - "dev": "vite", + "dev": "clear && vite", "dev:debug": "NODE_OPTIONS='--inspect-brk=9229 --enable-source-maps' vite", "command": "COMMAND_TESTER=true pnpm run build > /dev/null 2>&1 && NODE_ENV=development ./dist/cli.js", "command:raw": "./dist/cli.js", @@ -125,6 +125,7 @@ "nestjs-pino": "4.4.0", "node-cache": "5.1.2", "node-window-polyfill": "1.0.4", + "openid-client": "^6.6.2", "p-retry": "6.2.1", "passport-custom": "1.1.1", "passport-http-header-strategy": "1.1.0", diff --git a/api/src/__test__/graphql/resolvers/rclone-api.service.test.ts b/api/src/__test__/graphql/resolvers/rclone-api.service.test.ts index a9c741536..3370fffa4 100644 --- a/api/src/__test__/graphql/resolvers/rclone-api.service.test.ts +++ b/api/src/__test__/graphql/resolvers/rclone-api.service.test.ts @@ -34,6 +34,15 @@ vi.mock('@app/store/index.js', () => ({ }), }, })); +vi.mock('@app/environment.js', () => ({ + ENVIRONMENT: 'development', + environment: { + IS_MAIN_PROCESS: true, + }, +})); +vi.mock('@app/core/utils/files/file-exists.js', () => ({ + fileExists: vi.fn().mockResolvedValue(true), +})); // Mock NestJS Logger to suppress logs during tests vi.mock('@nestjs/common', async (importOriginal) => { @@ -63,13 +72,22 @@ describe('RCloneApiService', () => { const { execa } = await import('execa'); const pRetry = await import('p-retry'); const { existsSync } = await import('node:fs'); + const { fileExists } = await import('@app/core/utils/files/file-exists.js'); mockGot = vi.mocked(got); mockExeca = vi.mocked(execa); mockPRetry = vi.mocked(pRetry.default); mockExistsSync = vi.mocked(existsSync); - mockGot.post = vi.fn().mockResolvedValue({ body: {} }); + // Mock successful RClone API response for socket check + mockGot.post = vi.fn().mockResolvedValue({ body: { pid: 12345 } }); + + // Mock RClone binary exists check + vi.mocked(fileExists).mockResolvedValue(true); + + // Mock socket exists + mockExistsSync.mockReturnValue(true); + mockExeca.mockReturnValue({ on: vi.fn(), kill: vi.fn(), @@ -77,10 +95,12 @@ describe('RCloneApiService', () => { pid: 12345, } as any); mockPRetry.mockResolvedValue(undefined); - mockExistsSync.mockReturnValue(false); service = new RCloneApiService(); await service.onModuleInit(); + + // Reset the mock after initialization to prepare for test-specific responses + mockGot.post.mockClear(); }); describe('getProviders', () => { @@ -102,6 +122,9 @@ describe('RCloneApiService', () => { json: {}, responseType: 'json', enableUnixSockets: true, + headers: expect.objectContaining({ + Authorization: expect.stringMatching(/^Basic /), + }), }) ); }); @@ -129,6 +152,11 @@ describe('RCloneApiService', () => { 'http://unix:/tmp/rclone.sock:/config/listremotes', expect.objectContaining({ json: {}, + responseType: 'json', + enableUnixSockets: true, + headers: expect.objectContaining({ + Authorization: expect.stringMatching(/^Basic /), + }), }) ); }); @@ -155,6 +183,11 @@ describe('RCloneApiService', () => { 'http://unix:/tmp/rclone.sock:/config/get', expect.objectContaining({ json: { name: 'test-remote' }, + responseType: 'json', + enableUnixSockets: true, + headers: expect.objectContaining({ + Authorization: expect.stringMatching(/^Basic /), + }), }) ); }); @@ -193,6 +226,11 @@ describe('RCloneApiService', () => { type: 's3', parameters: { access_key_id: 'AKIA...', secret_access_key: 'secret' }, }, + responseType: 'json', + enableUnixSockets: true, + headers: expect.objectContaining({ + Authorization: expect.stringMatching(/^Basic /), + }), }) ); }); @@ -217,6 +255,11 @@ describe('RCloneApiService', () => { name: 'existing-remote', access_key_id: 'NEW_AKIA...', }, + responseType: 'json', + enableUnixSockets: true, + headers: expect.objectContaining({ + Authorization: expect.stringMatching(/^Basic /), + }), }) ); }); @@ -235,6 +278,11 @@ describe('RCloneApiService', () => { 'http://unix:/tmp/rclone.sock:/config/delete', expect.objectContaining({ json: { name: 'remote-to-delete' }, + responseType: 'json', + enableUnixSockets: true, + headers: expect.objectContaining({ + Authorization: expect.stringMatching(/^Basic /), + }), }) ); }); @@ -261,6 +309,11 @@ describe('RCloneApiService', () => { dstFs: 'remote:backup/path', delete_on: 'dst', }, + responseType: 'json', + enableUnixSockets: true, + headers: expect.objectContaining({ + Authorization: expect.stringMatching(/^Basic /), + }), }) ); }); @@ -279,6 +332,11 @@ describe('RCloneApiService', () => { 'http://unix:/tmp/rclone.sock:/job/status', expect.objectContaining({ json: { jobid: 'job-123' }, + responseType: 'json', + enableUnixSockets: true, + headers: expect.objectContaining({ + Authorization: expect.stringMatching(/^Basic /), + }), }) ); }); @@ -299,6 +357,11 @@ describe('RCloneApiService', () => { 'http://unix:/tmp/rclone.sock:/job/list', expect.objectContaining({ json: {}, + responseType: 'json', + enableUnixSockets: true, + headers: expect.objectContaining({ + Authorization: expect.stringMatching(/^Basic /), + }), }) ); }); diff --git a/api/src/__test__/setup.ts b/api/src/__test__/setup.ts index 00c9fbe05..4c6729b84 100644 --- a/api/src/__test__/setup.ts +++ b/api/src/__test__/setup.ts @@ -3,6 +3,7 @@ import '@app/__test__/setup/env-setup.js'; import '@app/__test__/setup/keyserver-mock.js'; import '@app/__test__/setup/config-setup.js'; import '@app/__test__/setup/store-reset.js'; +import '@app/__test__/setup/api-json-backup.js'; // This file is automatically loaded by Vitest before running tests // It imports all the setup files that need to be run before tests diff --git a/api/src/__test__/setup/api-json-backup.ts b/api/src/__test__/setup/api-json-backup.ts new file mode 100644 index 000000000..7b9bdd11b --- /dev/null +++ b/api/src/__test__/setup/api-json-backup.ts @@ -0,0 +1,36 @@ +import { existsSync, readFileSync, writeFileSync } from 'fs'; +import { join, resolve } from 'path'; + +import { afterAll, beforeAll } from 'vitest'; + +// Get the project root directory +const projectRoot = resolve(process.cwd()); +const apiJsonPath = join(projectRoot, 'dev/configs/api.json'); +const apiJsonBackupPath = join(projectRoot, 'dev/configs/api.json.backup'); + +let originalContent: string | null = null; + +/** + * Backs up api.json before tests run and restores it after tests complete. + * This prevents tests from permanently modifying the development configuration. + */ +export function setupApiJsonBackup() { + beforeAll(() => { + // Save the original content if the file exists + if (existsSync(apiJsonPath)) { + originalContent = readFileSync(apiJsonPath, 'utf-8'); + // Create a backup file as well for safety + writeFileSync(apiJsonBackupPath, originalContent, 'utf-8'); + } + }); + + afterAll(() => { + // Restore the original content if we saved it + if (originalContent !== null) { + writeFileSync(apiJsonPath, originalContent, 'utf-8'); + } + }); +} + +// Auto-run for all tests that import this module +setupApiJsonBackup(); diff --git a/api/src/unraid-api/app/app.module.ts b/api/src/unraid-api/app/app.module.ts index 9f7ebac1f..d472c78f0 100644 --- a/api/src/unraid-api/app/app.module.ts +++ b/api/src/unraid-api/app/app.module.ts @@ -28,18 +28,10 @@ import { UnraidFileModifierModule } from '@app/unraid-api/unraid-file-modifier/u logger: apiLogger, autoLogging: false, timestamp: false, - ...(LOG_LEVEL !== 'TRACE' - ? { - serializers: { - req: (req) => ({ - id: req.id, - method: req.method, - url: req.url, - remoteAddress: req.remoteAddress, - }), - }, - } - : {}), + serializers: { + req: () => undefined, + res: () => undefined, + }, }, }), AuthModule, diff --git a/api/src/unraid-api/cli/generated/gql.ts b/api/src/unraid-api/cli/generated/gql.ts index 7ab7b2e4f..1f89d183f 100644 --- a/api/src/unraid-api/cli/generated/gql.ts +++ b/api/src/unraid-api/cli/generated/gql.ts @@ -23,6 +23,7 @@ type Documents = { "\n query SystemReport {\n info {\n id\n machineId\n system {\n manufacturer\n model\n version\n sku\n serial\n uuid\n }\n versions {\n unraid\n kernel\n openssl\n }\n }\n config {\n id\n valid\n error\n }\n server {\n id\n name\n }\n }\n": typeof types.SystemReportDocument, "\n query ConnectStatus {\n connect {\n id\n dynamicRemoteAccess {\n enabledType\n runningType\n error\n }\n }\n }\n": typeof types.ConnectStatusDocument, "\n query Services {\n services {\n id\n name\n online\n uptime {\n timestamp\n }\n version\n }\n }\n": typeof types.ServicesDocument, + "\n query ValidateOidcSession($token: String!) {\n validateOidcSession(token: $token) {\n valid\n username\n }\n }\n": typeof types.ValidateOidcSessionDocument, }; const documents: Documents = { "\n mutation AddPlugin($input: PluginManagementInput!) {\n addPlugin(input: $input)\n }\n": types.AddPluginDocument, @@ -34,6 +35,7 @@ const documents: Documents = { "\n query SystemReport {\n info {\n id\n machineId\n system {\n manufacturer\n model\n version\n sku\n serial\n uuid\n }\n versions {\n unraid\n kernel\n openssl\n }\n }\n config {\n id\n valid\n error\n }\n server {\n id\n name\n }\n }\n": types.SystemReportDocument, "\n query ConnectStatus {\n connect {\n id\n dynamicRemoteAccess {\n enabledType\n runningType\n error\n }\n }\n }\n": types.ConnectStatusDocument, "\n query Services {\n services {\n id\n name\n online\n uptime {\n timestamp\n }\n version\n }\n }\n": types.ServicesDocument, + "\n query ValidateOidcSession($token: String!) {\n validateOidcSession(token: $token) {\n valid\n username\n }\n }\n": types.ValidateOidcSessionDocument, }; /** @@ -86,6 +88,10 @@ export function gql(source: "\n query ConnectStatus {\n connect {\n * The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ export function gql(source: "\n query Services {\n services {\n id\n name\n online\n uptime {\n timestamp\n }\n version\n }\n }\n"): (typeof documents)["\n query Services {\n services {\n id\n name\n online\n uptime {\n timestamp\n }\n version\n }\n }\n"]; +/** + * The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function gql(source: "\n query ValidateOidcSession($token: String!) {\n validateOidcSession(token: $token) {\n valid\n username\n }\n }\n"): (typeof documents)["\n query ValidateOidcSession($token: String!) {\n validateOidcSession(token: $token) {\n valid\n username\n }\n }\n"]; export function gql(source: string) { return (documents as any)[source] ?? {}; diff --git a/api/src/unraid-api/cli/generated/graphql.ts b/api/src/unraid-api/cli/generated/graphql.ts index 2596de020..10f256da5 100644 --- a/api/src/unraid-api/cli/generated/graphql.ts +++ b/api/src/unraid-api/cli/generated/graphql.ts @@ -385,6 +385,20 @@ export enum AuthPossession { OWN_ANY = 'OWN_ANY' } +/** Operators for authorization rule matching */ +export enum AuthorizationOperator { + CONTAINS = 'CONTAINS', + ENDS_WITH = 'ENDS_WITH', + EQUALS = 'EQUALS', + STARTS_WITH = 'STARTS_WITH' +} + +/** Mode for evaluating authorization rules - OR (any rule passes) or AND (all rules must pass) */ +export enum AuthorizationRuleMode { + AND = 'AND', + OR = 'OR' +} + export type Baseboard = Node & { __typename?: 'Baseboard'; assetTag?: Maybe; @@ -940,21 +954,25 @@ export type Mutation = { configureUps: Scalars['Boolean']['output']; connectSignIn: Scalars['Boolean']['output']; connectSignOut: Scalars['Boolean']['output']; + createDockerFolder: ResolvedOrganizerV1; /** Creates a new notification record */ createNotification: Notification; /** Deletes all archived notifications on server. */ deleteArchivedNotifications: NotificationOverview; + deleteDockerEntries: ResolvedOrganizerV1; deleteNotification: NotificationOverview; docker: DockerMutations; enableDynamicRemoteAccess: Scalars['Boolean']['output']; /** Initiates a flash drive backup using a configured remote. */ initiateFlashBackup: FlashBackupStatus; + moveDockerEntriesToFolder: ResolvedOrganizerV1; parityCheck: ParityCheckMutations; rclone: RCloneMutations; /** Reads each notification to recompute & update the overview. */ recalculateOverview: NotificationOverview; /** Remove one or more plugins from the API. Returns false if restart was triggered automatically, true if manual restart is required. */ removePlugin: Scalars['Boolean']['output']; + setDockerFolderChildren: ResolvedOrganizerV1; setupRemoteAccess: Scalars['Boolean']['output']; unarchiveAll: NotificationOverview; unarchiveNotifications: NotificationOverview; @@ -996,11 +1014,23 @@ export type MutationConnectSignInArgs = { }; +export type MutationCreateDockerFolderArgs = { + childrenIds?: InputMaybe>; + name: Scalars['String']['input']; + parentId?: InputMaybe; +}; + + export type MutationCreateNotificationArgs = { input: NotificationData; }; +export type MutationDeleteDockerEntriesArgs = { + entryIds: Array; +}; + + export type MutationDeleteNotificationArgs = { id: Scalars['PrefixedID']['input']; type: NotificationType; @@ -1017,11 +1047,23 @@ export type MutationInitiateFlashBackupArgs = { }; +export type MutationMoveDockerEntriesToFolderArgs = { + destinationFolderId: Scalars['String']['input']; + sourceEntryIds: Array; +}; + + export type MutationRemovePluginArgs = { input: PluginManagementInput; }; +export type MutationSetDockerFolderChildrenArgs = { + childrenIds: Array; + folderId?: InputMaybe; +}; + + export type MutationSetupRemoteAccessArgs = { input: SetupRemoteAccessInput; }; @@ -1129,12 +1171,62 @@ export type NotificationsListArgs = { filter: NotificationFilter; }; +export type OidcAuthorizationRule = { + __typename?: 'OidcAuthorizationRule'; + /** The claim to check (e.g., email, sub, groups, hd) */ + claim: Scalars['String']['output']; + /** The comparison operator */ + operator: AuthorizationOperator; + /** The value(s) to match against */ + value: Array; +}; + +export type OidcProvider = { + __typename?: 'OidcProvider'; + /** OAuth2 authorization endpoint URL. If omitted, will be auto-discovered from issuer/.well-known/openid-configuration */ + authorizationEndpoint?: Maybe; + /** Mode for evaluating authorization rules - OR (any rule passes) or AND (all rules must pass). Defaults to OR. */ + authorizationRuleMode?: Maybe; + /** Flexible authorization rules based on claims */ + authorizationRules?: Maybe>; + /** URL or base64 encoded icon for the login button */ + buttonIcon?: Maybe; + /** Custom CSS styles for the button (e.g., "background: linear-gradient(to right, #4f46e5, #7c3aed); border-radius: 9999px;") */ + buttonStyle?: Maybe; + /** Custom text for the login button */ + buttonText?: Maybe; + /** Button variant style from Reka UI. See https://reka-ui.com/docs/components/button */ + buttonVariant?: Maybe; + /** OAuth2 client ID registered with the provider */ + clientId: Scalars['String']['output']; + /** OAuth2 client secret (if required by provider) */ + clientSecret?: Maybe; + /** 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']; + /** JSON Web Key Set URI for token validation. If omitted, will be auto-discovered from issuer/.well-known/openid-configuration */ + jwksUri?: Maybe; + /** Display name of the OIDC provider */ + name: Scalars['String']['output']; + /** OAuth2 scopes to request (e.g., openid, profile, email) */ + scopes: Array; + /** OAuth2 token endpoint URL. If omitted, will be auto-discovered from issuer/.well-known/openid-configuration */ + tokenEndpoint?: Maybe; +}; + +export type OidcSessionValidation = { + __typename?: 'OidcSessionValidation'; + username?: Maybe; + valid: Scalars['Boolean']['output']; +}; + export type OrganizerContainerResource = { __typename?: 'OrganizerContainerResource'; id: Scalars['String']['output']; meta?: Maybe; name: Scalars['String']['output']; - type: OrganizerResourceType; + type: Scalars['String']['output']; }; export type OrganizerResource = { @@ -1142,17 +1234,9 @@ export type OrganizerResource = { id: Scalars['String']['output']; meta?: Maybe; name: Scalars['String']['output']; - type: OrganizerResourceType; + type: Scalars['String']['output']; }; -/** The type of organizer resource */ -export enum OrganizerResourceType { - BOOKMARK = 'BOOKMARK', - CONTAINER = 'CONTAINER', - FILE = 'FILE', - VM = 'VM' -} - export type Os = Node & { __typename?: 'Os'; arch?: Maybe; @@ -1266,6 +1350,16 @@ export type ProfileModel = Node & { username: Scalars['String']['output']; }; +export type PublicOidcProvider = { + __typename?: 'PublicOidcProvider'; + buttonIcon?: Maybe; + buttonStyle?: Maybe; + buttonText?: Maybe; + buttonVariant?: Maybe; + id: Scalars['ID']['output']; + name: Scalars['String']['output']; +}; + export type PublicPartnerInfo = { __typename?: 'PublicPartnerInfo'; /** Indicates if a partner logo exists */ @@ -1303,11 +1397,17 @@ export type Query = { network: Network; /** Get all notifications */ notifications: Notifications; + /** Get a specific OIDC provider by ID */ + oidcProvider?: Maybe; + /** Get all configured OIDC providers (admin only) */ + oidcProviders: Array; online: Scalars['Boolean']['output']; owner: Owner; parityHistory: Array; /** List all installed plugins with their metadata */ plugins: Array; + /** Get public OIDC provider information for login buttons */ + publicOidcProviders: Array; publicPartnerInfo?: Maybe; publicTheme: Theme; rclone: RCloneBackupSettings; @@ -1321,6 +1421,8 @@ export type Query = { upsConfiguration: UpsConfiguration; upsDeviceById?: Maybe; upsDevices: Array; + /** Validate an OIDC session token (internal use for CLI validation) */ + validateOidcSession: OidcSessionValidation; vars: Vars; /** Get information about all VMs on the system */ vms: Vms; @@ -1344,10 +1446,20 @@ export type QueryLogFileArgs = { }; +export type QueryOidcProviderArgs = { + id: Scalars['PrefixedID']['input']; +}; + + export type QueryUpsDeviceByIdArgs = { id: Scalars['String']['input']; }; + +export type QueryValidateOidcSessionArgs = { + token: Scalars['String']['input']; +}; + export type RCloneBackupConfigForm = { __typename?: 'RCloneBackupConfigForm'; dataSchema: Scalars['JSON']['output']; @@ -1571,6 +1683,8 @@ export type Settings = Node & { /** The API setting values */ api: ApiConfig; id: Scalars['PrefixedID']['output']; + /** SSO settings */ + sso: SsoSettings; /** A view of all settings */ unified: UnifiedSettings; }; @@ -1619,6 +1733,13 @@ export type Share = Node & { used?: Maybe; }; +export type SsoSettings = Node & { + __typename?: 'SsoSettings'; + id: Scalars['PrefixedID']['output']; + /** List of configured OIDC providers */ + oidcProviders: Array; +}; + export type Subscription = { __typename?: 'Subscription'; arraySubscription: UnraidArray; @@ -1855,6 +1976,8 @@ export type UpdateSettingsResponse = { restartRequired: Scalars['Boolean']['output']; /** The updated settings values */ values: Scalars['JSON']['output']; + /** Warning messages about configuration issues found during validation */ + warnings?: Maybe>; }; export type Uptime = { @@ -2238,6 +2361,13 @@ export type ServicesQueryVariables = Exact<{ [key: string]: never; }>; export type ServicesQuery = { __typename?: 'Query', services: Array<{ __typename?: 'Service', id: any, name?: string | null, online?: boolean | null, version?: string | null, uptime?: { __typename?: 'Uptime', timestamp?: string | null } | null }> }; +export type ValidateOidcSessionQueryVariables = Exact<{ + token: Scalars['String']['input']; +}>; + + +export type ValidateOidcSessionQuery = { __typename?: 'Query', validateOidcSession: { __typename?: 'OidcSessionValidation', valid: boolean, username?: string | null } }; + export const AddPluginDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"AddPlugin"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"PluginManagementInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"addPlugin"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} as unknown as DocumentNode; export const RemovePluginDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"RemovePlugin"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"PluginManagementInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"removePlugin"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} as unknown as DocumentNode; @@ -2247,4 +2377,5 @@ export const GetPluginsDocument = {"kind":"Document","definitions":[{"kind":"Ope export const GetSsoUsersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetSSOUsers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"settings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"api"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ssoSubIds"}}]}}]}}]}}]} as unknown as DocumentNode; export const SystemReportDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"SystemReport"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"info"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"machineId"}},{"kind":"Field","name":{"kind":"Name","value":"system"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"manufacturer"}},{"kind":"Field","name":{"kind":"Name","value":"model"}},{"kind":"Field","name":{"kind":"Name","value":"version"}},{"kind":"Field","name":{"kind":"Name","value":"sku"}},{"kind":"Field","name":{"kind":"Name","value":"serial"}},{"kind":"Field","name":{"kind":"Name","value":"uuid"}}]}},{"kind":"Field","name":{"kind":"Name","value":"versions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"unraid"}},{"kind":"Field","name":{"kind":"Name","value":"kernel"}},{"kind":"Field","name":{"kind":"Name","value":"openssl"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"config"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"valid"}},{"kind":"Field","name":{"kind":"Name","value":"error"}}]}},{"kind":"Field","name":{"kind":"Name","value":"server"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]} as unknown as DocumentNode; export const ConnectStatusDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ConnectStatus"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"connect"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"dynamicRemoteAccess"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"enabledType"}},{"kind":"Field","name":{"kind":"Name","value":"runningType"}},{"kind":"Field","name":{"kind":"Name","value":"error"}}]}}]}}]}}]} as unknown as DocumentNode; -export const ServicesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Services"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"services"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"online"}},{"kind":"Field","name":{"kind":"Name","value":"uptime"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"timestamp"}}]}},{"kind":"Field","name":{"kind":"Name","value":"version"}}]}}]}}]} as unknown as DocumentNode; \ No newline at end of file +export const ServicesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Services"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"services"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"online"}},{"kind":"Field","name":{"kind":"Name","value":"uptime"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"timestamp"}}]}},{"kind":"Field","name":{"kind":"Name","value":"version"}}]}}]}}]} as unknown as DocumentNode; +export const ValidateOidcSessionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ValidateOidcSession"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"token"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"validateOidcSession"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"token"},"value":{"kind":"Variable","name":{"kind":"Name","value":"token"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"valid"}},{"kind":"Field","name":{"kind":"Name","value":"username"}}]}}]}}]} as unknown as DocumentNode; \ No newline at end of file diff --git a/api/src/unraid-api/cli/queries/validate-oidc-session.query.ts b/api/src/unraid-api/cli/queries/validate-oidc-session.query.ts new file mode 100644 index 000000000..3a14ba214 --- /dev/null +++ b/api/src/unraid-api/cli/queries/validate-oidc-session.query.ts @@ -0,0 +1,10 @@ +import { gql } from '@app/unraid-api/cli/generated/index.js'; + +export const VALIDATE_OIDC_SESSION_QUERY = gql(` + query ValidateOidcSession($token: String!) { + validateOidcSession(token: $token) { + valid + username + } + } +`); diff --git a/api/src/unraid-api/cli/sso/validate-token.command.ts b/api/src/unraid-api/cli/sso/validate-token.command.ts index 32cc1826c..0440c4831 100644 --- a/api/src/unraid-api/cli/sso/validate-token.command.ts +++ b/api/src/unraid-api/cli/sso/validate-token.command.ts @@ -1,11 +1,8 @@ -import type { JWTPayload } from 'jose'; -import { createLocalJWKSet, createRemoteJWKSet, jwtVerify } from 'jose'; import { CommandRunner, SubCommand } from 'nest-commander'; -import { JWKS_LOCAL_PAYLOAD, JWKS_REMOTE_LINK } from '@app/consts.js'; import { CliInternalClientService } from '@app/unraid-api/cli/internal-client.service.js'; import { LogService } from '@app/unraid-api/cli/log.service.js'; -import { SSO_USERS_QUERY } from '@app/unraid-api/cli/queries/sso-users.query.js'; +import { VALIDATE_OIDC_SESSION_QUERY } from '@app/unraid-api/cli/queries/validate-oidc-session.query.js'; @SubCommand({ name: 'validate-token', @@ -14,15 +11,11 @@ import { SSO_USERS_QUERY } from '@app/unraid-api/cli/queries/sso-users.query.js' arguments: '', }) export class ValidateTokenCommand extends CommandRunner { - JWKSOffline: ReturnType; - JWKSOnline: ReturnType; constructor( private readonly logger: LogService, private readonly internalClient: CliInternalClientService ) { super(); - this.JWKSOffline = createLocalJWKSet(JWKS_LOCAL_PAYLOAD); - this.JWKSOnline = createRemoteJWKSet(new URL(JWKS_REMOTE_LINK)); } private createErrorAndExit = (errorMessage: string) => { @@ -46,68 +39,40 @@ export class ValidateTokenCommand extends CommandRunner { this.createErrorAndExit('Invalid token provided'); } - if (!/^[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+$/.test(token)) { - this.createErrorAndExit('Token format is invalid'); - } + // Always validate as OIDC token + await this.validateOidcToken(token); + } - let caughtError: null | unknown = null; - let tokenPayload: null | JWTPayload = null; + private async validateOidcToken(token: string): Promise { try { - // this.logger.debug('Attempting to validate token with local key'); - tokenPayload = (await jwtVerify(token, this.JWKSOffline)).payload; - } catch (error: unknown) { - try { - // this.logger.debug('Local validation failed for key, trying remote validation'); - tokenPayload = (await jwtVerify(token, this.JWKSOnline)).payload; - } catch (error: unknown) { - caughtError = error; - } - } - - if (caughtError) { - if (caughtError instanceof Error) { - this.createErrorAndExit(`Caught error validating jwt token: ${caughtError.message}`); - } else { - this.createErrorAndExit('Caught unknown error validating jwt token'); - } - } - - if (tokenPayload === null) { - this.createErrorAndExit('No data in JWT to use for user validation'); - } - - const username = tokenPayload?.sub; - - if (!username) { - return this.createErrorAndExit('No ID found in token'); - } - const client = await this.internalClient.getClient(); - - let result; - try { - result = await client.query({ - query: SSO_USERS_QUERY, + const client = await this.internalClient.getClient(); + const { data, errors } = await client.query({ + query: VALIDATE_OIDC_SESSION_QUERY, + variables: { token }, }); + + if (errors?.length) { + const errorMessages = errors.map((e) => e.message).join(', '); + this.createErrorAndExit(`GraphQL errors: ${errorMessages}`); + } + + const validation = data?.validateOidcSession; + + if (validation?.valid) { + this.logger.always( + JSON.stringify({ + error: null, + valid: true, + username: validation.username || 'root', + }) + ); + process.exit(0); + } else { + this.createErrorAndExit('Invalid OIDC session token'); + } } catch (error) { - this.createErrorAndExit('Failed to query SSO users'); - } - - if (result.errors && result.errors.length > 0) { - this.createErrorAndExit('Failed to retrieve SSO configuration'); - } - - const ssoUsers = result.data?.settings?.api?.ssoSubIds || []; - - if (ssoUsers.length === 0) { - this.createErrorAndExit( - 'No local user token set to compare to - please set any valid SSO IDs you would like to sign in with' - ); - } - if (ssoUsers.includes(username)) { - this.logger.always(JSON.stringify({ error: null, valid: true, username })); - process.exit(0); - } else { - this.createErrorAndExit('Username on token does not match'); + const errorMessage = error instanceof Error ? error.message : String(error); + this.createErrorAndExit(`Failed to validate OIDC session: ${errorMessage}`); } } } diff --git a/api/src/unraid-api/graph/graph.module.ts b/api/src/unraid-api/graph/graph.module.ts index 03217bfdd..554489aaf 100644 --- a/api/src/unraid-api/graph/graph.module.ts +++ b/api/src/unraid-api/graph/graph.module.ts @@ -12,6 +12,7 @@ import { NoUnusedVariablesRule } from 'graphql'; import { ENVIRONMENT } from '@app/environment.js'; import { ApiConfigModule } from '@app/unraid-api/config/api-config.module.js'; +import { createDynamicIntrospectionPlugin } from '@app/unraid-api/graph/introspection-plugin.js'; import { ResolversModule } from '@app/unraid-api/graph/resolvers/resolvers.module.js'; import { createSandboxPlugin } from '@app/unraid-api/graph/sandbox-plugin.js'; import { GlobalDepsModule } from '@app/unraid-api/plugin/global-deps.module.js'; @@ -34,7 +35,7 @@ import { PluginModule } from '@app/unraid-api/plugin/plugin.module.js'; path: './generated-schema.graphql', } : true, - introspection: isSandboxEnabled(), + introspection: true, playground: false, // we handle this in the sandbox plugin context: async ({ req, connectionParams, extra }) => { return { @@ -43,7 +44,10 @@ import { PluginModule } from '@app/unraid-api/plugin/plugin.module.js'; extra, }; }, - plugins: [createSandboxPlugin(isSandboxEnabled)] as any[], + plugins: [ + createDynamicIntrospectionPlugin(isSandboxEnabled), + createSandboxPlugin(), + ] as any[], subscriptions: { 'graphql-ws': { path: '/graphql', diff --git a/api/src/unraid-api/graph/introspection-plugin.spec.ts b/api/src/unraid-api/graph/introspection-plugin.spec.ts new file mode 100644 index 000000000..3def3549d --- /dev/null +++ b/api/src/unraid-api/graph/introspection-plugin.spec.ts @@ -0,0 +1,271 @@ +import { describe, expect, it, vi } from 'vitest'; + +import { createDynamicIntrospectionPlugin } from '@app/unraid-api/graph/introspection-plugin.js'; + +describe('Dynamic Introspection Plugin', () => { + const mockResponse = () => ({ + body: null as any, + http: { + status: 200, + }, + }); + + const runPlugin = async ( + query: string | undefined, + operationName: string | undefined, + sandboxEnabled: boolean + ) => { + const isSandboxEnabled = vi.fn().mockReturnValue(sandboxEnabled); + const plugin = createDynamicIntrospectionPlugin(isSandboxEnabled); + + const response = mockResponse(); + const requestContext = { + request: { + query, + operationName, + }, + response, + } as any; + + const requestListener = await (plugin as any).requestDidStart(); + await requestListener.willSendResponse(requestContext); + + return response; + }; + + describe('when sandbox is enabled', () => { + it('should allow introspection query with IntrospectionQuery operation name', async () => { + const response = await runPlugin( + 'query IntrospectionQuery { __schema { queryType { name } } }', + 'IntrospectionQuery', + true + ); + + expect(response.http.status).toBe(200); + expect(response.body).toBeNull(); + }); + + it('should allow direct __schema query', async () => { + const response = await runPlugin('{ __schema { queryType { name } } }', undefined, true); + + expect(response.http.status).toBe(200); + expect(response.body).toBeNull(); + }); + + it('should allow regular queries with __type field', async () => { + const response = await runPlugin( + 'query GetType { __type(name: "User") { name fields { name } } }', + 'GetType', + true + ); + + expect(response.http.status).toBe(200); + expect(response.body).toBeNull(); + }); + }); + + describe('when sandbox is disabled', () => { + it('should block introspection query with IntrospectionQuery operation name', async () => { + const response = await runPlugin( + 'query IntrospectionQuery { __schema { queryType { name } } }', + 'IntrospectionQuery', + false + ); + + expect(response.http.status).toBe(400); + expect(response.body).toEqual({ + kind: 'single', + singleResult: { + errors: [ + { + message: + 'GraphQL introspection is not allowed, but the current request is for introspection.', + extensions: { + code: 'INTROSPECTION_DISABLED', + }, + }, + ], + }, + }); + }); + + it('should block direct __schema query', async () => { + const response = await runPlugin('{ __schema { queryType { name } } }', undefined, false); + + expect(response.http.status).toBe(400); + expect(response.body?.singleResult?.errors?.[0]?.extensions?.code).toBe( + 'INTROSPECTION_DISABLED' + ); + }); + + it('should block __schema query with whitespace variations', async () => { + const queries = [ + '{__schema{queryType{name}}}', + '{ __schema { queryType { name } } }', + '{\n __schema\n {\n queryType\n {\n name\n }\n }\n}', + 'query { __schema { types { name } } }', + 'query MyQuery { __schema { directives { name } } }', + ]; + + for (const query of queries) { + const response = await runPlugin(query, undefined, false); + expect(response.http.status).toBe(400); + expect(response.body?.singleResult?.errors?.[0]?.extensions?.code).toBe( + 'INTROSPECTION_DISABLED' + ); + } + }); + + it('should allow regular queries without introspection', async () => { + const response = await runPlugin( + 'query GetUser { user(id: "123") { name email } }', + 'GetUser', + false + ); + + expect(response.http.status).toBe(200); + expect(response.body).toBeNull(); + }); + + it('should allow queries with __type field (not full introspection)', async () => { + const response = await runPlugin( + 'query GetType { __type(name: "User") { name fields { name } } }', + 'GetType', + false + ); + + expect(response.http.status).toBe(200); + expect(response.body).toBeNull(); + }); + + it('should allow queries with __typename field', async () => { + const response = await runPlugin( + 'query GetUser { user(id: "123") { __typename name email } }', + 'GetUser', + false + ); + + expect(response.http.status).toBe(200); + expect(response.body).toBeNull(); + }); + + it('should allow mutations', async () => { + const response = await runPlugin( + 'mutation CreateUser($input: UserInput!) { createUser(input: $input) { id name } }', + 'CreateUser', + false + ); + + expect(response.http.status).toBe(200); + expect(response.body).toBeNull(); + }); + + it('should allow subscriptions', async () => { + const response = await runPlugin( + 'subscription OnUserCreated { userCreated { id name } }', + 'OnUserCreated', + false + ); + + expect(response.http.status).toBe(200); + expect(response.body).toBeNull(); + }); + + it('should not block when __schema appears in a string or comment', async () => { + const response = await runPlugin( + 'query GetUser { user(id: "123") { name description } } # __schema is mentioned here', + 'GetUser', + false + ); + + expect(response.http.status).toBe(200); + expect(response.body).toBeNull(); + }); + + it('should handle missing query gracefully', async () => { + const response = await runPlugin(undefined, undefined, false); + + expect(response.http.status).toBe(200); + expect(response.body).toBeNull(); + }); + + it('should handle empty query gracefully', async () => { + const response = await runPlugin('', undefined, false); + + expect(response.http.status).toBe(200); + expect(response.body).toBeNull(); + }); + }); + + describe('edge cases', () => { + it('should handle response without http property', async () => { + const isSandboxEnabled = vi.fn().mockReturnValue(false); + const plugin = createDynamicIntrospectionPlugin(isSandboxEnabled); + + const response = { body: null as any }; + const requestContext = { + request: { + query: '{ __schema { queryType { name } } }', + operationName: undefined, + }, + response, + } as any; + + const requestListener = await (plugin as any).requestDidStart(); + await requestListener.willSendResponse(requestContext); + + expect(response.body).toEqual({ + kind: 'single', + singleResult: { + errors: [ + { + message: + 'GraphQL introspection is not allowed, but the current request is for introspection.', + extensions: { + code: 'INTROSPECTION_DISABLED', + }, + }, + ], + }, + }); + // Should not throw even though response.http doesn't exist + }); + + it('should check sandbox status dynamically on each request', async () => { + const isSandboxEnabled = vi.fn(); + const plugin = createDynamicIntrospectionPlugin(isSandboxEnabled); + + // First request - sandbox disabled + isSandboxEnabled.mockReturnValue(false); + const response1 = mockResponse(); + const requestContext1 = { + request: { + query: '{ __schema { queryType { name } } }', + operationName: undefined, + }, + response: response1, + } as any; + + let requestListener = await (plugin as any).requestDidStart(); + await requestListener.willSendResponse(requestContext1); + expect(response1.http.status).toBe(400); + + // Second request - sandbox enabled + isSandboxEnabled.mockReturnValue(true); + const response2 = mockResponse(); + const requestContext2 = { + request: { + query: '{ __schema { queryType { name } } }', + operationName: undefined, + }, + response: response2, + } as any; + + requestListener = await (plugin as any).requestDidStart(); + await requestListener.willSendResponse(requestContext2); + expect(response2.http.status).toBe(200); + + expect(isSandboxEnabled).toHaveBeenCalledTimes(2); + }); + }); +}); diff --git a/api/src/unraid-api/graph/introspection-plugin.ts b/api/src/unraid-api/graph/introspection-plugin.ts new file mode 100644 index 000000000..c178fc858 --- /dev/null +++ b/api/src/unraid-api/graph/introspection-plugin.ts @@ -0,0 +1,43 @@ +import type { ApolloServerPlugin, GraphQLRequestListener } from '@apollo/server'; + +export const createDynamicIntrospectionPlugin = ( + isSandboxEnabled: () => boolean +): ApolloServerPlugin => ({ + requestDidStart: async () => + ({ + willSendResponse: async (requestContext) => { + const { request, response } = requestContext; + + // Detect introspection queries: + // 1. Standard operation name "IntrospectionQuery" + // 2. Queries containing __schema at root level (main introspection entry point) + // Note: __type and __typename are also used in regular queries, so we don't block them + const isIntrospectionRequest = + request.operationName === 'IntrospectionQuery' || + (request.query && + // Check for __schema which is the main introspection entry point + // Match patterns like: { __schema { ... } } or query { __schema { ... } } + /\{\s*__schema\s*[{(]/.test(request.query)); + + if (isIntrospectionRequest && !isSandboxEnabled()) { + response.body = { + kind: 'single', + singleResult: { + errors: [ + { + message: + 'GraphQL introspection is not allowed, but the current request is for introspection.', + extensions: { + code: 'INTROSPECTION_DISABLED', + }, + }, + ], + }, + }; + if (response.http) { + response.http.status = 400; + } + } + }, + }) satisfies GraphQLRequestListener, +}); diff --git a/api/src/unraid-api/graph/resolvers/rclone/rclone-api.service.ts b/api/src/unraid-api/graph/resolvers/rclone/rclone-api.service.ts index 550feb8f1..89fd362e9 100644 --- a/api/src/unraid-api/graph/resolvers/rclone/rclone-api.service.ts +++ b/api/src/unraid-api/graph/resolvers/rclone/rclone-api.service.ts @@ -10,13 +10,13 @@ import pRetry from 'p-retry'; import { sanitizeParams } from '@app/core/log.js'; import { fileExists } from '@app/core/utils/files/file-exists.js'; +import { ENVIRONMENT } from '@app/environment.js'; import { CreateRCloneRemoteDto, DeleteRCloneRemoteDto, GetRCloneJobStatusDto, GetRCloneRemoteConfigDto, GetRCloneRemoteDetailsDto, - RCloneProviderOptionResponse, RCloneProviderResponse, RCloneRemoteConfig, RCloneStartBackupInput, @@ -45,6 +45,13 @@ export class RCloneApiService implements OnModuleInit, OnModuleDestroy { } async onModuleInit(): Promise { + // RClone startup disabled - early return + if (ENVIRONMENT === 'production') { + this.logger.debug('RClone startup is disabled'); + this.isInitialized = false; + return; + } + try { // Check if rclone binary is available first const isBinaryAvailable = await this.checkRcloneBinaryExists(); @@ -356,7 +363,9 @@ export class RCloneApiService implements OnModuleInit, OnModuleDestroy { * Generic method to call the RClone RC API */ private async callRcloneApi(endpoint: string, params: Record = {}): Promise { - const url = `${this.rcloneBaseUrl}/${endpoint}`; + // Ensure endpoint starts with '/' for proper Unix socket URL format + const normalizedEndpoint = endpoint.startsWith('/') ? endpoint : `/${endpoint}`; + const url = `${this.rcloneBaseUrl}${normalizedEndpoint}`; try { this.logger.debug( `Calling RClone API: ${url} with params: ${JSON.stringify(sanitizeParams(params))}` diff --git a/api/src/unraid-api/graph/resolvers/resolvers.module.ts b/api/src/unraid-api/graph/resolvers/resolvers.module.ts index 49bb543df..e29746ba5 100644 --- a/api/src/unraid-api/graph/resolvers/resolvers.module.ts +++ b/api/src/unraid-api/graph/resolvers/resolvers.module.ts @@ -27,6 +27,7 @@ import { RCloneModule } from '@app/unraid-api/graph/resolvers/rclone/rclone.modu import { RegistrationResolver } from '@app/unraid-api/graph/resolvers/registration/registration.resolver.js'; import { ServerResolver } from '@app/unraid-api/graph/resolvers/servers/server.resolver.js'; import { SettingsModule } from '@app/unraid-api/graph/resolvers/settings/settings.module.js'; +import { SsoModule } from '@app/unraid-api/graph/resolvers/sso/sso.module.js'; import { UPSModule } from '@app/unraid-api/graph/resolvers/ups/ups.module.js'; import { VarsResolver } from '@app/unraid-api/graph/resolvers/vars/vars.resolver.js'; import { VmMutationsResolver } from '@app/unraid-api/graph/resolvers/vms/vms.mutations.resolver.js'; @@ -47,6 +48,7 @@ import { MeResolver } from '@app/unraid-api/graph/user/user.resolver.js'; FlashBackupModule, RCloneModule, SettingsModule, + SsoModule, UPSModule, ], providers: [ diff --git a/api/src/unraid-api/graph/resolvers/settings/settings.model.ts b/api/src/unraid-api/graph/resolvers/settings/settings.model.ts index 922ce0970..7e71e78b7 100644 --- a/api/src/unraid-api/graph/resolvers/settings/settings.model.ts +++ b/api/src/unraid-api/graph/resolvers/settings/settings.model.ts @@ -4,6 +4,8 @@ import { Node } from '@unraid/shared/graphql.model.js'; import { IsObject, ValidateNested } from 'class-validator'; import { GraphQLJSON } from 'graphql-scalars'; +import { SsoSettings } from '@app/unraid-api/graph/resolvers/settings/sso-settings.model.js'; + @ObjectType({ implements: () => Node, }) @@ -30,6 +32,12 @@ export class UpdateSettingsResponse { @Field(() => GraphQLJSON, { description: 'The updated settings values' }) values!: Record; + + @Field(() => [String], { + nullable: true, + description: 'Warning messages about configuration issues found during validation', + }) + warnings?: string[]; } @ObjectType({ @@ -39,4 +47,8 @@ export class Settings extends Node { @Field(() => UnifiedSettings, { description: 'A view of all settings' }) @ValidateNested() unified!: UnifiedSettings; + + @Field(() => SsoSettings, { description: 'SSO settings' }) + @ValidateNested() + sso!: SsoSettings; } diff --git a/api/src/unraid-api/graph/resolvers/settings/settings.module.ts b/api/src/unraid-api/graph/resolvers/settings/settings.module.ts index 5682657dd..795c5b7ac 100644 --- a/api/src/unraid-api/graph/resolvers/settings/settings.module.ts +++ b/api/src/unraid-api/graph/resolvers/settings/settings.module.ts @@ -5,14 +5,28 @@ import { UserSettingsModule } from '@unraid/shared/services/user-settings.js'; import { SsoUserService } from '@app/unraid-api/auth/sso-user.service.js'; import { SettingsResolver, + SsoSettingsResolver, UnifiedSettingsResolver, } from '@app/unraid-api/graph/resolvers/settings/settings.resolver.js'; import { ApiSettings } from '@app/unraid-api/graph/resolvers/settings/settings.service.js'; +import { SsoModule } from '@app/unraid-api/graph/resolvers/sso/sso.module.js'; import { UnraidFileModifierModule } from '@app/unraid-api/unraid-file-modifier/unraid-file-modifier.module.js'; @Module({ - imports: [UserSettingsModule, UnraidFileModifierModule], - providers: [SettingsResolver, UnifiedSettingsResolver, SsoUserService, ApiSettings], - exports: [SettingsResolver, UnifiedSettingsResolver, UserSettingsModule, ApiSettings], + imports: [UserSettingsModule, UnraidFileModifierModule, SsoModule], + providers: [ + SettingsResolver, + UnifiedSettingsResolver, + SsoSettingsResolver, + SsoUserService, + ApiSettings, + ], + exports: [ + SettingsResolver, + UnifiedSettingsResolver, + SsoSettingsResolver, + UserSettingsModule, + ApiSettings, + ], }) export class SettingsModule {} diff --git a/api/src/unraid-api/graph/resolvers/settings/settings.resolver.ts b/api/src/unraid-api/graph/resolvers/settings/settings.resolver.ts index 1ab1f5201..5db104cb1 100644 --- a/api/src/unraid-api/graph/resolvers/settings/settings.resolver.ts +++ b/api/src/unraid-api/graph/resolvers/settings/settings.resolver.ts @@ -21,12 +21,16 @@ import { UpdateSettingsResponse, } from '@app/unraid-api/graph/resolvers/settings/settings.model.js'; import { ApiSettings } from '@app/unraid-api/graph/resolvers/settings/settings.service.js'; +import { SsoSettings } from '@app/unraid-api/graph/resolvers/settings/sso-settings.model.js'; +import { OidcConfigPersistence } from '@app/unraid-api/graph/resolvers/sso/oidc-config.service.js'; +import { OidcProvider } from '@app/unraid-api/graph/resolvers/sso/oidc-provider.model.js'; @Resolver(() => Settings) export class SettingsResolver { constructor( private readonly apiSettings: ApiSettings, - private readonly ssoUserService: SsoUserService + private readonly ssoUserService: SsoUserService, + private readonly oidcConfig: OidcConfigPersistence ) {} @Query(() => Settings) @@ -51,6 +55,13 @@ export class SettingsResolver { }; } + @ResolveField(() => SsoSettings) + async sso() { + return { + id: 'sso-settings', + }; + } + @Query(() => Boolean) @Public() public async isSSOEnabled(): Promise { @@ -68,7 +79,7 @@ export class UnifiedSettingsResolver { @ResolveField(() => GraphQLJSON) async dataSchema() { - const { properties } = await this.userSettings.getAllSettings(['api']); + const { properties } = await this.userSettings.getAllSettings(['api', 'sso']); return { type: 'object', properties, @@ -77,7 +88,7 @@ export class UnifiedSettingsResolver { @ResolveField(() => GraphQLJSON) async uiSchema() { - const { elements } = await this.userSettings.getAllSettings(['api']); + const { elements } = await this.userSettings.getAllSettings(['api', 'sso']); return { type: 'VerticalLayout', elements, @@ -96,7 +107,7 @@ export class UnifiedSettingsResolver { possession: AuthPossession.ANY, }) async updateSettings( - @Args('input', { type: () => GraphQLJSON }) input: object + @Args('input', { type: () => GraphQLJSON }) input: Record ): Promise { this.logger.verbose('Updating Settings %O', input); const { restartRequired, values } = await this.userSettings.updateNamespacedValues(input); @@ -108,3 +119,13 @@ export class UnifiedSettingsResolver { return { restartRequired, values }; } } + +@Resolver(() => SsoSettings) +export class SsoSettingsResolver { + constructor(private readonly oidcConfig: OidcConfigPersistence) {} + + @ResolveField(() => [OidcProvider], { description: 'List of configured OIDC providers' }) + async oidcProviders(): Promise { + return this.oidcConfig.getProviders(); + } +} diff --git a/api/src/unraid-api/graph/resolvers/settings/settings.service.ts b/api/src/unraid-api/graph/resolvers/settings/settings.service.ts index 7b5365aff..0bc2850a2 100644 --- a/api/src/unraid-api/graph/resolvers/settings/settings.service.ts +++ b/api/src/unraid-api/graph/resolvers/settings/settings.service.ts @@ -8,6 +8,7 @@ import { UserSettingsService } from '@unraid/shared/services/user-settings.js'; import { execa } from 'execa'; import { SsoUserService } from '@app/unraid-api/auth/sso-user.service.js'; +import { OidcConfigPersistence } from '@app/unraid-api/graph/resolvers/sso/oidc-config.service.js'; import { createLabeledControl } from '@app/unraid-api/graph/utils/form-utils.js'; import { SettingSlice } from '@app/unraid-api/types/json-forms.js'; @@ -17,7 +18,8 @@ export class ApiSettings { constructor( private readonly userSettings: UserSettingsService, private readonly configService: ConfigService<{ api: ApiConfig }, true>, - private readonly ssoUserService: SsoUserService + private readonly ssoUserService: SsoUserService, + private readonly oidcConfig: OidcConfigPersistence ) { this.userSettings.register('api', { buildSlice: async () => this.buildSlice(), @@ -26,6 +28,19 @@ export class ApiSettings { }); } + private async shouldShowSsoUsersSettings(): Promise { + // Check if OIDC config exists, which means migration has happened + try { + const { access } = await import('fs/promises'); + await access(this.oidcConfig.configPath()); + // File exists, migration has happened + return false; + } catch { + // File doesn't exist, show the setting + return true; + } + } + getSettings(): ApiConfig { return { version: this.configService.get('api.version', { infer: true }), @@ -37,16 +52,13 @@ export class ApiSettings { } async updateSettings(settings: Partial) { - let restartRequired = false; + const restartRequired = false; if (typeof settings.sandbox === 'boolean') { - const currentSandbox = this.configService.get('api.sandbox', { infer: true }); - restartRequired ||= settings.sandbox !== currentSandbox; // @ts-expect-error - depend on the configService.get calls above for type safety this.configService.set('api.sandbox', settings.sandbox); } if (settings.ssoSubIds) { - const ssoNeedsRestart = await this.ssoUserService.setSsoUsers(settings.ssoSubIds); - restartRequired ||= ssoNeedsRestart; + await this.ssoUserService.setSsoUsers(settings.ssoSubIds); } if (settings.extraOrigins) { // @ts-expect-error - this is correct, but the configService typescript implementation is too narrow @@ -55,17 +67,19 @@ export class ApiSettings { return { restartRequired, values: await this.getSettings() }; } - buildSlice(): SettingSlice { - return mergeSettingSlices( - [ - this.sandboxSlice(), - this.ssoUsersSlice(), - // Because CORS is effectively disabled, this setting is no longer necessary - // keeping it here for in case it needs to be re-enabled - // this.extraOriginsSlice(), - ], - { as: 'api' } - ); + async buildSlice(): Promise { + const slices: SettingSlice[] = [this.sandboxSlice()]; + + // Only show SSO users setting if migration hasn't happened yet + if (await this.shouldShowSsoUsersSettings()) { + slices.push(this.ssoUsersSlice()); + } + + // Because CORS is effectively disabled, this setting is no longer necessary + // keeping it here for in case it needs to be re-enabled + // slices.push(this.extraOriginsSlice()); + + return mergeSettingSlices(slices, { as: 'api' }); } /** @@ -98,7 +112,6 @@ export class ApiSettings { /** * Extra origins settings slice - */ private extraOriginsSlice(): SettingSlice { return { properties: { @@ -126,7 +139,7 @@ export class ApiSettings { }), ], }; - } + } */ /** * SSO users settings slice @@ -140,14 +153,14 @@ export class ApiSettings { type: 'string', }, title: 'Unraid API SSO Users', - description: `Provide a list of Unique Unraid Account ID's. Find yours at
account.unraid.net/settings. Requires restart if adding first user.`, + description: `Provide a list of Unique Unraid Account ID's. Find yours at account.unraid.net/settings.`, }, }, elements: [ createLabeledControl({ scope: '#/properties/api/properties/ssoSubIds', label: 'Unraid Connect SSO Users:', - description: `Provide a list of Unique Unraid Account IDs. Find yours at account.unraid.net/settings. Requires restart if adding first user.`, + description: `Provide a list of Unique Unraid Account IDs. Find yours at account.unraid.net/settings.`, controlOptions: { inputType: 'text', placeholder: 'UUID', diff --git a/api/src/unraid-api/graph/resolvers/settings/sso-settings.model.ts b/api/src/unraid-api/graph/resolvers/settings/sso-settings.model.ts new file mode 100644 index 000000000..30274fcc5 --- /dev/null +++ b/api/src/unraid-api/graph/resolvers/settings/sso-settings.model.ts @@ -0,0 +1,10 @@ +import { ObjectType } from '@nestjs/graphql'; + +import { Node } from '@unraid/shared/graphql.model.js'; + +@ObjectType({ + implements: () => Node, +}) +export class SsoSettings extends Node { + // oidcProviders field is resolved via SsoSettingsResolver +} diff --git a/api/src/unraid-api/graph/resolvers/sso/oidc-auth.service.test.ts b/api/src/unraid-api/graph/resolvers/sso/oidc-auth.service.test.ts new file mode 100644 index 000000000..8039438d9 --- /dev/null +++ b/api/src/unraid-api/graph/resolvers/sso/oidc-auth.service.test.ts @@ -0,0 +1,1542 @@ +import { UnauthorizedException } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { Test, TestingModule } from '@nestjs/testing'; + +import * as client from 'openid-client'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import { OidcAuthService } from '@app/unraid-api/graph/resolvers/sso/oidc-auth.service.js'; +import { OidcConfigPersistence } from '@app/unraid-api/graph/resolvers/sso/oidc-config.service.js'; +import { + AuthorizationOperator, + AuthorizationRuleMode, + OidcAuthorizationRule, + OidcProvider, +} from '@app/unraid-api/graph/resolvers/sso/oidc-provider.model.js'; +import { OidcSessionService } from '@app/unraid-api/graph/resolvers/sso/oidc-session.service.js'; +import { OidcStateService } from '@app/unraid-api/graph/resolvers/sso/oidc-state.service.js'; +import { OidcValidationService } from '@app/unraid-api/graph/resolvers/sso/oidc-validation.service.js'; + +describe('OidcAuthService', () => { + let service: OidcAuthService; + let oidcConfig: any; + let sessionService: any; + let configService: any; + let stateService: any; + let validationService: any; + let module: TestingModule; + + beforeEach(async () => { + module = await Test.createTestingModule({ + providers: [ + OidcAuthService, + { + provide: ConfigService, + useValue: { + get: vi.fn(), + }, + }, + { + provide: OidcConfigPersistence, + useValue: { + getProvider: vi.fn(), + }, + }, + { + provide: OidcSessionService, + useValue: { + createSession: vi.fn(), + }, + }, + OidcStateService, + { + provide: OidcValidationService, + useValue: { + validateProvider: vi.fn(), + performDiscovery: vi.fn(), + }, + }, + ], + }).compile(); + + service = module.get(OidcAuthService); + oidcConfig = module.get(OidcConfigPersistence); + sessionService = module.get(OidcSessionService); + configService = module.get(ConfigService); + stateService = module.get(OidcStateService); + validationService = module.get(OidcValidationService); + }); + + describe('Authorization Rule Evaluation', () => { + // Access the private method through any type casting for testing + const evaluateRule = (rule: OidcAuthorizationRule, claims: any): boolean => { + return (service as any).evaluateRule(rule, claims); + }; + + describe('EQUALS operator', () => { + it('should return true when claim equals any value in the array', () => { + const rule: OidcAuthorizationRule = { + claim: 'email', + operator: AuthorizationOperator.EQUALS, + value: ['user@example.com', 'admin@example.com'], + }; + + expect(evaluateRule(rule, { email: 'user@example.com' })).toBe(true); + expect(evaluateRule(rule, { email: 'admin@example.com' })).toBe(true); + }); + + it('should return false when claim does not equal any value', () => { + const rule: OidcAuthorizationRule = { + claim: 'email', + operator: AuthorizationOperator.EQUALS, + value: ['user@example.com'], + }; + + expect(evaluateRule(rule, { email: 'other@example.com' })).toBe(false); + }); + + it('should handle numeric values correctly', () => { + const rule: OidcAuthorizationRule = { + claim: 'user_id', + operator: AuthorizationOperator.EQUALS, + value: ['12345'], + }; + + expect(evaluateRule(rule, { user_id: 12345 })).toBe(true); + expect(evaluateRule(rule, { user_id: '12345' })).toBe(true); + }); + }); + + describe('CONTAINS operator', () => { + it('should return true when claim contains any substring', () => { + const rule: OidcAuthorizationRule = { + claim: 'email', + operator: AuthorizationOperator.CONTAINS, + value: ['@company.com', '@partner.org'], + }; + + expect(evaluateRule(rule, { email: 'john@company.com' })).toBe(true); + expect(evaluateRule(rule, { email: 'jane@partner.org' })).toBe(true); + }); + + it('should return false when claim does not contain any substring', () => { + const rule: OidcAuthorizationRule = { + claim: 'email', + operator: AuthorizationOperator.CONTAINS, + value: ['@company.com'], + }; + + expect(evaluateRule(rule, { email: 'user@other.org' })).toBe(false); + }); + + it('should be case sensitive', () => { + const rule: OidcAuthorizationRule = { + claim: 'name', + operator: AuthorizationOperator.CONTAINS, + value: ['Admin'], + }; + + expect(evaluateRule(rule, { name: 'Administrator' })).toBe(true); + expect(evaluateRule(rule, { name: 'administrator' })).toBe(false); + }); + }); + + describe('STARTS_WITH operator', () => { + it('should return true when claim starts with any prefix', () => { + const rule: OidcAuthorizationRule = { + claim: 'department', + operator: AuthorizationOperator.STARTS_WITH, + value: ['eng-', 'dev-'], + }; + + expect(evaluateRule(rule, { department: 'eng-backend' })).toBe(true); + expect(evaluateRule(rule, { department: 'dev-frontend' })).toBe(true); + }); + + it('should return false when claim does not start with any prefix', () => { + const rule: OidcAuthorizationRule = { + claim: 'department', + operator: AuthorizationOperator.STARTS_WITH, + value: ['eng-'], + }; + + expect(evaluateRule(rule, { department: 'marketing-team' })).toBe(false); + expect(evaluateRule(rule, { department: 'backend-eng' })).toBe(false); + }); + }); + + describe('ENDS_WITH operator', () => { + it('should return true when claim ends with any suffix', () => { + const rule: OidcAuthorizationRule = { + claim: 'email', + operator: AuthorizationOperator.ENDS_WITH, + value: ['@gmail.com', '@company.com'], + }; + + expect(evaluateRule(rule, { email: 'user@gmail.com' })).toBe(true); + expect(evaluateRule(rule, { email: 'admin@company.com' })).toBe(true); + }); + + it('should return false when claim does not end with any suffix', () => { + const rule: OidcAuthorizationRule = { + claim: 'email', + operator: AuthorizationOperator.ENDS_WITH, + value: ['@company.com'], + }; + + expect(evaluateRule(rule, { email: 'user@other.org' })).toBe(false); + }); + + it('should handle domain validation correctly', () => { + const rule: OidcAuthorizationRule = { + claim: 'hd', + operator: AuthorizationOperator.ENDS_WITH, + value: ['.edu'], + }; + + expect(evaluateRule(rule, { hd: 'university.edu' })).toBe(true); + expect(evaluateRule(rule, { hd: 'college.edu' })).toBe(true); + expect(evaluateRule(rule, { hd: 'company.com' })).toBe(false); + }); + }); + + describe('Missing or undefined claims', () => { + it('should return false when claim is missing', () => { + const rule: OidcAuthorizationRule = { + claim: 'email', + operator: AuthorizationOperator.EQUALS, + value: ['user@example.com'], + }; + + expect(evaluateRule(rule, {})).toBe(false); + expect(evaluateRule(rule, { name: 'John' })).toBe(false); + }); + + it('should return false when claim is null', () => { + const rule: OidcAuthorizationRule = { + claim: 'email', + operator: AuthorizationOperator.EQUALS, + value: ['user@example.com'], + }; + + expect(evaluateRule(rule, { email: null })).toBe(false); + }); + + it('should return false when claim is undefined', () => { + const rule: OidcAuthorizationRule = { + claim: 'email', + operator: AuthorizationOperator.EQUALS, + value: ['user@example.com'], + }; + + expect(evaluateRule(rule, { email: undefined })).toBe(false); + }); + }); + + describe('Type coercion', () => { + it('should convert values to strings for comparison', () => { + const rule: OidcAuthorizationRule = { + claim: 'user_id', + operator: AuthorizationOperator.EQUALS, + value: ['12345'], + }; + + expect(evaluateRule(rule, { user_id: 12345 })).toBe(true); + expect(evaluateRule(rule, { user_id: '12345' })).toBe(true); + }); + + it('should handle boolean values', () => { + const rule: OidcAuthorizationRule = { + claim: 'is_admin', + operator: AuthorizationOperator.EQUALS, + value: ['true'], + }; + + expect(evaluateRule(rule, { is_admin: true })).toBe(true); + expect(evaluateRule(rule, { is_admin: 'true' })).toBe(true); + expect(evaluateRule(rule, { is_admin: false })).toBe(false); + }); + }); + + describe('Multiple rules evaluation', () => { + // Access the private method through any type casting for testing + const evaluateAuthorizationRules = ( + rules: OidcAuthorizationRule[], + claims: any, + mode: AuthorizationRuleMode = AuthorizationRuleMode.OR + ): boolean => { + return (service as any).evaluateAuthorizationRules(rules, claims, mode); + }; + + it('should require ANY rule to pass (OR logic)', () => { + const rules: OidcAuthorizationRule[] = [ + { + claim: 'email', + operator: AuthorizationOperator.ENDS_WITH, + value: ['@company.com'], + }, + { + claim: 'department', + operator: AuthorizationOperator.EQUALS, + value: ['engineering', 'development'], + }, + ]; + + // Both rules pass + expect( + evaluateAuthorizationRules(rules, { + email: 'user@company.com', + department: 'engineering', + }) + ).toBe(true); + + // Only email rule passes + expect( + evaluateAuthorizationRules(rules, { + email: 'user@company.com', + department: 'marketing', + }) + ).toBe(true); + + // Only department rule passes + expect( + evaluateAuthorizationRules(rules, { + email: 'user@other.com', + department: 'engineering', + }) + ).toBe(true); + + // Neither rule passes + expect( + evaluateAuthorizationRules(rules, { + email: 'user@other.com', + department: 'marketing', + }) + ).toBe(false); + }); + + it('should return false when no rules are defined', () => { + expect(evaluateAuthorizationRules([], { email: 'any@email.com' })).toBe(false); + }); + + describe('AND logic mode', () => { + it('should require ALL rules to pass when using AND mode', () => { + const rules: OidcAuthorizationRule[] = [ + { + claim: 'email', + operator: AuthorizationOperator.ENDS_WITH, + value: ['@company.com'], + }, + { + claim: 'department', + operator: AuthorizationOperator.EQUALS, + value: ['engineering'], + }, + ]; + + // Both rules pass - should return true + expect( + evaluateAuthorizationRules( + rules, + { + email: 'user@company.com', + department: 'engineering', + }, + AuthorizationRuleMode.AND + ) + ).toBe(true); + + // Only email rule passes - should return false + expect( + evaluateAuthorizationRules( + rules, + { + email: 'user@company.com', + department: 'marketing', + }, + AuthorizationRuleMode.AND + ) + ).toBe(false); + + // Only department rule passes - should return false + expect( + evaluateAuthorizationRules( + rules, + { + email: 'user@other.com', + department: 'engineering', + }, + AuthorizationRuleMode.AND + ) + ).toBe(false); + + // Neither rule passes - should return false + expect( + evaluateAuthorizationRules( + rules, + { + email: 'user@other.com', + department: 'marketing', + }, + AuthorizationRuleMode.AND + ) + ).toBe(false); + }); + + it('should handle complex AND conditions with multiple operators', () => { + const rules: OidcAuthorizationRule[] = [ + { + claim: 'email', + operator: AuthorizationOperator.ENDS_WITH, + value: ['@company.com'], + }, + { + claim: 'name', + operator: AuthorizationOperator.STARTS_WITH, + value: ['John', 'Jane'], + }, + { + claim: 'role', + operator: AuthorizationOperator.CONTAINS, + value: ['admin', 'manager'], + }, + ]; + + // All rules pass + expect( + evaluateAuthorizationRules( + rules, + { + email: 'john.doe@company.com', + name: 'John Doe', + role: 'senior-admin', + }, + AuthorizationRuleMode.AND + ) + ).toBe(true); + + // Missing one condition (role doesn't contain admin/manager) + expect( + evaluateAuthorizationRules( + rules, + { + email: 'john.doe@company.com', + name: 'John Doe', + role: 'developer', + }, + AuthorizationRuleMode.AND + ) + ).toBe(false); + }); + + it('should return true with single rule in AND mode', () => { + const rules: OidcAuthorizationRule[] = [ + { + claim: 'email', + operator: AuthorizationOperator.EQUALS, + value: ['admin@company.com'], + }, + ]; + + expect( + evaluateAuthorizationRules( + rules, + { email: 'admin@company.com' }, + AuthorizationRuleMode.AND + ) + ).toBe(true); + + expect( + evaluateAuthorizationRules( + rules, + { email: 'user@company.com' }, + AuthorizationRuleMode.AND + ) + ).toBe(false); + }); + }); + + describe('OR logic mode (default)', () => { + it('should handle complex OR conditions with multiple operators', () => { + const rules: OidcAuthorizationRule[] = [ + { + claim: 'email', + operator: AuthorizationOperator.EQUALS, + value: ['admin@company.com', 'superuser@company.com'], + }, + { + claim: 'groups', + operator: AuthorizationOperator.CONTAINS, + value: ['administrators', 'power-users'], + }, + { + claim: 'department', + operator: AuthorizationOperator.STARTS_WITH, + value: ['IT-', 'SEC-'], + }, + ]; + + // Multiple rules pass + expect( + evaluateAuthorizationRules( + rules, + { + email: 'admin@company.com', + groups: 'administrators', + department: 'IT-Support', + }, + AuthorizationRuleMode.OR + ) + ).toBe(true); + + // Only one rule passes (department) + expect( + evaluateAuthorizationRules( + rules, + { + email: 'user@company.com', + groups: 'users', + department: 'SEC-Operations', + }, + AuthorizationRuleMode.OR + ) + ).toBe(true); + + // No rules pass + expect( + evaluateAuthorizationRules( + rules, + { + email: 'user@company.com', + groups: 'users', + department: 'HR-Management', + }, + AuthorizationRuleMode.OR + ) + ).toBe(false); + }); + + it('should use OR mode as default when mode is not specified', () => { + const rules: OidcAuthorizationRule[] = [ + { + claim: 'email', + operator: AuthorizationOperator.ENDS_WITH, + value: ['@company.com'], + }, + { + claim: 'special_user', + operator: AuthorizationOperator.EQUALS, + value: ['true'], + }, + ]; + + // Test without explicit mode (should default to OR) + expect( + evaluateAuthorizationRules(rules, { + email: 'user@other.com', + special_user: 'true', + }) + ).toBe(true); + }); + }); + + describe('Edge cases and boundary conditions', () => { + it('should handle empty rules array correctly', () => { + expect( + evaluateAuthorizationRules( + [], + { email: 'any@email.com' }, + AuthorizationRuleMode.AND + ) + ).toBe(false); + expect( + evaluateAuthorizationRules( + [], + { email: 'any@email.com' }, + AuthorizationRuleMode.OR + ) + ).toBe(false); + }); + + it('should handle rules with empty value arrays', () => { + const rules: OidcAuthorizationRule[] = [ + { + claim: 'email', + operator: AuthorizationOperator.EQUALS, + value: [], + }, + ]; + + expect( + evaluateAuthorizationRules( + rules, + { email: 'user@company.com' }, + AuthorizationRuleMode.OR + ) + ).toBe(false); + + expect( + evaluateAuthorizationRules( + rules, + { email: 'user@company.com' }, + AuthorizationRuleMode.AND + ) + ).toBe(false); + }); + + it('should handle missing claims in AND mode', () => { + const rules: OidcAuthorizationRule[] = [ + { + claim: 'email', + operator: AuthorizationOperator.ENDS_WITH, + value: ['@company.com'], + }, + { + claim: 'department', + operator: AuthorizationOperator.EQUALS, + value: ['engineering'], + }, + ]; + + // Missing department claim + expect( + evaluateAuthorizationRules( + rules, + { email: 'user@company.com' }, + AuthorizationRuleMode.AND + ) + ).toBe(false); + }); + + it('should handle missing claims in OR mode', () => { + const rules: OidcAuthorizationRule[] = [ + { + claim: 'email', + operator: AuthorizationOperator.ENDS_WITH, + value: ['@company.com'], + }, + { + claim: 'department', + operator: AuthorizationOperator.EQUALS, + value: ['engineering'], + }, + ]; + + // Missing department claim but email passes + expect( + evaluateAuthorizationRules( + rules, + { email: 'user@company.com' }, + AuthorizationRuleMode.OR + ) + ).toBe(true); + + // Missing both claims + expect( + evaluateAuthorizationRules(rules, { other: 'value' }, AuthorizationRuleMode.OR) + ).toBe(false); + }); + + it('should handle all claims missing in both modes', () => { + const rules: OidcAuthorizationRule[] = [ + { + claim: 'email', + operator: AuthorizationOperator.EQUALS, + value: ['admin@company.com'], + }, + { + claim: 'role', + operator: AuthorizationOperator.EQUALS, + value: ['admin'], + }, + ]; + + const claimsWithoutRequired = { name: 'John', sub: '12345' }; + + expect( + evaluateAuthorizationRules( + rules, + claimsWithoutRequired, + AuthorizationRuleMode.AND + ) + ).toBe(false); + + expect( + evaluateAuthorizationRules( + rules, + claimsWithoutRequired, + AuthorizationRuleMode.OR + ) + ).toBe(false); + }); + }); + + describe('Real-world authorization scenarios', () => { + it('should handle Google Workspace domain + specific users (OR)', () => { + const rules: OidcAuthorizationRule[] = [ + { + claim: 'hd', + operator: AuthorizationOperator.EQUALS, + value: ['company.com'], + }, + { + claim: 'email', + operator: AuthorizationOperator.EQUALS, + value: ['contractor1@gmail.com', 'contractor2@outlook.com'], + }, + ]; + + // Company domain user + expect( + evaluateAuthorizationRules( + rules, + { hd: 'company.com', email: 'employee@company.com' }, + AuthorizationRuleMode.OR + ) + ).toBe(true); + + // Allowed contractor + expect( + evaluateAuthorizationRules( + rules, + { email: 'contractor1@gmail.com' }, + AuthorizationRuleMode.OR + ) + ).toBe(true); + + // Unauthorized user + expect( + evaluateAuthorizationRules( + rules, + { email: 'random@gmail.com' }, + AuthorizationRuleMode.OR + ) + ).toBe(false); + }); + + it('should handle department + role requirements (AND)', () => { + const rules: OidcAuthorizationRule[] = [ + { + claim: 'department', + operator: AuthorizationOperator.EQUALS, + value: ['engineering', 'devops'], + }, + { + claim: 'role', + operator: AuthorizationOperator.CONTAINS, + value: ['senior', 'lead', 'manager'], + }, + ]; + + // Senior engineer - both conditions met + expect( + evaluateAuthorizationRules( + rules, + { department: 'engineering', role: 'senior-engineer' }, + AuthorizationRuleMode.AND + ) + ).toBe(true); + + // Junior engineer - department ok, but not senior + expect( + evaluateAuthorizationRules( + rules, + { department: 'engineering', role: 'junior-engineer' }, + AuthorizationRuleMode.AND + ) + ).toBe(false); + + // Senior in wrong department + expect( + evaluateAuthorizationRules( + rules, + { department: 'marketing', role: 'senior-marketer' }, + AuthorizationRuleMode.AND + ) + ).toBe(false); + }); + + it('should handle email domain + group membership (AND)', () => { + const rules: OidcAuthorizationRule[] = [ + { + claim: 'email', + operator: AuthorizationOperator.ENDS_WITH, + value: ['@trusted.com', '@partner.com'], + }, + { + claim: 'groups', + operator: AuthorizationOperator.CONTAINS, + value: ['vpn-access'], + }, + ]; + + // Trusted domain with VPN access + expect( + evaluateAuthorizationRules( + rules, + { email: 'user@trusted.com', groups: 'vpn-access,developers' }, + AuthorizationRuleMode.AND + ) + ).toBe(true); + + // Trusted domain without VPN access + expect( + evaluateAuthorizationRules( + rules, + { email: 'user@trusted.com', groups: 'developers' }, + AuthorizationRuleMode.AND + ) + ).toBe(false); + }); + }); + }); + + describe('Real-world scenarios', () => { + it('should authorize Google Workspace users by domain', () => { + const rule: OidcAuthorizationRule = { + claim: 'hd', + operator: AuthorizationOperator.EQUALS, + value: ['company.com'], + }; + + expect(evaluateRule(rule, { hd: 'company.com', email: 'user@company.com' })).toBe(true); + expect(evaluateRule(rule, { hd: 'other.com', email: 'user@other.com' })).toBe(false); + }); + + it('should authorize users by email domain pattern', () => { + const rule: OidcAuthorizationRule = { + claim: 'email', + operator: AuthorizationOperator.ENDS_WITH, + value: ['@company.com', '@subsidiary.company.com'], + }; + + expect(evaluateRule(rule, { email: 'john@company.com' })).toBe(true); + expect(evaluateRule(rule, { email: 'jane@subsidiary.company.com' })).toBe(true); + expect(evaluateRule(rule, { email: 'external@gmail.com' })).toBe(false); + }); + + it('should authorize specific users by subject ID', () => { + const rule: OidcAuthorizationRule = { + claim: 'sub', + operator: AuthorizationOperator.EQUALS, + value: ['user123', 'user456', 'user789'], + }; + + expect(evaluateRule(rule, { sub: 'user456' })).toBe(true); + expect(evaluateRule(rule, { sub: 'user999' })).toBe(false); + }); + + it('should authorize users in specific groups', () => { + const rule: OidcAuthorizationRule = { + claim: 'groups', + operator: AuthorizationOperator.CONTAINS, + value: ['admin', 'developer'], + }; + + // When groups is a string containing the role + expect(evaluateRule(rule, { groups: 'user,admin,viewer' })).toBe(true); + expect(evaluateRule(rule, { groups: 'developer,tester' })).toBe(true); + expect(evaluateRule(rule, { groups: 'user,viewer' })).toBe(false); + }); + }); + }); + + describe('checkAuthorization', () => { + // Access the private method through any type casting for testing + const checkAuthorization = async (provider: OidcProvider, claims: any): Promise => { + return (service as any).checkAuthorization(provider, claims); + }; + + it('should throw error when no authorization rules are configured', async () => { + const provider: OidcProvider = { + id: 'test', + name: 'Test Provider', + clientId: 'test-client', + issuer: 'https://test.com', + scopes: ['openid'], + authorizationRules: [], + } as OidcProvider; + + await expect(checkAuthorization(provider, { sub: 'user123' })).rejects.toThrow(); + }); + + it('should throw error when authorization rules do not match', async () => { + const provider: OidcProvider = { + id: 'test', + name: 'Test Provider', + clientId: 'test-client', + issuer: 'https://test.com', + scopes: ['openid'], + authorizationRules: [ + { + claim: 'email', + operator: AuthorizationOperator.ENDS_WITH, + value: ['@company.com'], + }, + ], + } as OidcProvider; + + await expect(checkAuthorization(provider, { email: 'user@other.com' })).rejects.toThrow( + new UnauthorizedException( + 'Access denied: Your account does not meet the authorization requirements for Test Provider.' + ) + ); + }); + + it('should not throw when authorization rules match', async () => { + const provider: OidcProvider = { + id: 'test', + name: 'Test Provider', + clientId: 'test-client', + issuer: 'https://test.com', + scopes: ['openid'], + authorizationRules: [ + { + claim: 'email', + operator: AuthorizationOperator.ENDS_WITH, + value: ['@company.com'], + }, + ], + } as OidcProvider; + + await expect( + checkAuthorization(provider, { email: 'user@company.com' }) + ).resolves.toBeUndefined(); + }); + + it('should authorize when ANY rule matches (OR logic)', async () => { + const provider: OidcProvider = { + id: 'test', + name: 'Test Provider', + clientId: 'test-client', + issuer: 'https://test.com', + scopes: ['openid'], + authorizationRules: [ + { + claim: 'email', + operator: AuthorizationOperator.ENDS_WITH, + value: ['@company.com'], + }, + { + claim: 'email', + operator: AuthorizationOperator.ENDS_WITH, + value: ['@partner.com'], + }, + { + claim: 'sub', + operator: AuthorizationOperator.EQUALS, + value: ['specific-user-id'], + }, + ], + } as OidcProvider; + + // Should pass with @partner.com email (second rule) + await expect( + checkAuthorization(provider, { email: 'user@partner.com', sub: 'other-id' }) + ).resolves.toBeUndefined(); + + // Should pass with specific sub (third rule) + await expect( + checkAuthorization(provider, { email: 'user@external.com', sub: 'specific-user-id' }) + ).resolves.toBeUndefined(); + + // Should fail when no rules match + await expect( + checkAuthorization(provider, { email: 'user@external.com', sub: 'other-id' }) + ).rejects.toThrow(); + }); + + it('should authorize using AND mode when all rules match', async () => { + const provider: OidcProvider = { + id: 'test', + name: 'Test Provider', + clientId: 'test-client', + issuer: 'https://test.com', + scopes: ['openid'], + authorizationRuleMode: AuthorizationRuleMode.AND, + authorizationRules: [ + { + claim: 'email', + operator: AuthorizationOperator.ENDS_WITH, + value: ['@company.com'], + }, + { + claim: 'department', + operator: AuthorizationOperator.EQUALS, + value: ['engineering'], + }, + ], + } as OidcProvider; + + // Should pass when both rules match + await expect( + checkAuthorization(provider, { + email: 'user@company.com', + department: 'engineering', + }) + ).resolves.toBeUndefined(); + }); + + it('should reject using AND mode when only some rules match', async () => { + const provider: OidcProvider = { + id: 'test', + name: 'Test Provider', + clientId: 'test-client', + issuer: 'https://test.com', + scopes: ['openid'], + authorizationRuleMode: AuthorizationRuleMode.AND, + authorizationRules: [ + { + claim: 'email', + operator: AuthorizationOperator.ENDS_WITH, + value: ['@company.com'], + }, + { + claim: 'department', + operator: AuthorizationOperator.EQUALS, + value: ['engineering'], + }, + ], + } as OidcProvider; + + // Should fail when only first rule matches + await expect( + checkAuthorization(provider, { + email: 'user@company.com', + department: 'marketing', + }) + ).rejects.toThrow(); + + // Should fail when only second rule matches + await expect( + checkAuthorization(provider, { + email: 'user@external.com', + department: 'engineering', + }) + ).rejects.toThrow(); + }); + + it('should default to OR mode when authorizationRuleMode is not specified', async () => { + const provider: OidcProvider = { + id: 'test', + name: 'Test Provider', + clientId: 'test-client', + issuer: 'https://test.com', + scopes: ['openid'], + // authorizationRuleMode not specified, should default to OR + authorizationRules: [ + { + claim: 'email', + operator: AuthorizationOperator.ENDS_WITH, + value: ['@company.com'], + }, + { + claim: 'department', + operator: AuthorizationOperator.EQUALS, + value: ['engineering'], + }, + ], + } as OidcProvider; + + // Should pass when only first rule matches (OR mode default) + await expect( + checkAuthorization(provider, { + email: 'user@company.com', + department: 'marketing', + }) + ).resolves.toBeUndefined(); + }); + + it('should work with OR mode explicitly set', async () => { + const provider: OidcProvider = { + id: 'test', + name: 'Test Provider', + clientId: 'test-client', + issuer: 'https://test.com', + scopes: ['openid'], + authorizationRuleMode: AuthorizationRuleMode.OR, + authorizationRules: [ + { + claim: 'email', + operator: AuthorizationOperator.ENDS_WITH, + value: ['@company.com'], + }, + { + claim: 'department', + operator: AuthorizationOperator.EQUALS, + value: ['engineering'], + }, + ], + } as OidcProvider; + + // Should pass when only first rule matches + await expect( + checkAuthorization(provider, { + email: 'user@company.com', + department: 'marketing', + }) + ).resolves.toBeUndefined(); + + // Should pass when only second rule matches + await expect( + checkAuthorization(provider, { + email: 'user@external.com', + department: 'engineering', + }) + ).resolves.toBeUndefined(); + }); + }); + + describe('Manual Configuration (No Discovery)', () => { + it('should create manual configuration when discovery fails but manual endpoints are provided', async () => { + const provider: OidcProvider = { + id: 'manual-provider', + name: 'Manual Provider', + clientId: 'test-client-id', + clientSecret: 'test-client-secret', + issuer: 'https://manual.example.com', + authorizationEndpoint: 'https://manual.example.com/auth', + tokenEndpoint: 'https://manual.example.com/token', + jwksUri: 'https://manual.example.com/jwks', + scopes: ['openid', 'profile'], + authorizationRules: [], + }; + + oidcConfig.getProvider.mockResolvedValue(provider); + + // Mock discovery to fail + validationService.performDiscovery = vi + .fn() + .mockRejectedValue(new Error('Discovery failed')); + + // Access the private method + const getOrCreateConfig = async (provider: OidcProvider) => { + return (service as any).getOrCreateConfig(provider); + }; + + const config = await getOrCreateConfig(provider); + + // Verify the configuration was created with the correct endpoints + expect(config).toBeDefined(); + expect(config.serverMetadata().authorization_endpoint).toBe( + 'https://manual.example.com/auth' + ); + expect(config.serverMetadata().token_endpoint).toBe('https://manual.example.com/token'); + expect(config.serverMetadata().jwks_uri).toBe('https://manual.example.com/jwks'); + expect(config.serverMetadata().issuer).toBe('https://manual.example.com'); + }); + + it('should create manual configuration with fallback issuer when not provided', async () => { + const provider: OidcProvider = { + id: 'manual-provider-no-issuer', + name: 'Manual Provider No Issuer', + clientId: 'test-client-id', + clientSecret: 'test-client-secret', + issuer: '', // Empty issuer should skip discovery and use manual endpoints + authorizationEndpoint: 'https://manual.example.com/auth', + tokenEndpoint: 'https://manual.example.com/token', + scopes: ['openid', 'profile'], + authorizationRules: [], + }; + + oidcConfig.getProvider.mockResolvedValue(provider); + + // No need to mock discovery since it won't be called with empty issuer + + // Access the private method + const getOrCreateConfig = async (provider: OidcProvider) => { + return (service as any).getOrCreateConfig(provider); + }; + + const config = await getOrCreateConfig(provider); + + // Verify the configuration was created with fallback issuer + expect(config).toBeDefined(); + expect(config.serverMetadata().issuer).toBe('manual-manual-provider-no-issuer'); + expect(config.serverMetadata().authorization_endpoint).toBe( + 'https://manual.example.com/auth' + ); + expect(config.serverMetadata().token_endpoint).toBe('https://manual.example.com/token'); + }); + + it('should handle manual configuration with client secret properly', async () => { + const provider: OidcProvider = { + id: 'manual-with-secret', + name: 'Manual With Secret', + clientId: 'test-client-id', + clientSecret: 'secret-123', + issuer: 'https://manual.example.com', + authorizationEndpoint: 'https://manual.example.com/auth', + tokenEndpoint: 'https://manual.example.com/token', + scopes: ['openid', 'profile'], + authorizationRules: [], + }; + + oidcConfig.getProvider.mockResolvedValue(provider); + + // Mock discovery to fail + validationService.performDiscovery = vi + .fn() + .mockRejectedValue(new Error('Discovery failed')); + + // Access the private method + const getOrCreateConfig = async (provider: OidcProvider) => { + return (service as any).getOrCreateConfig(provider); + }; + + const config = await getOrCreateConfig(provider); + + // Verify configuration was created successfully + expect(config).toBeDefined(); + expect(config.clientMetadata().client_secret).toBe('secret-123'); + }); + + it('should handle manual configuration without client secret (public client)', async () => { + const provider: OidcProvider = { + id: 'manual-public-client', + name: 'Manual Public Client', + clientId: 'public-client-id', + // No client secret + issuer: 'https://manual.example.com', + authorizationEndpoint: 'https://manual.example.com/auth', + tokenEndpoint: 'https://manual.example.com/token', + scopes: ['openid', 'profile'], + authorizationRules: [], + }; + + oidcConfig.getProvider.mockResolvedValue(provider); + + // Mock discovery to fail + validationService.performDiscovery = vi + .fn() + .mockRejectedValue(new Error('Discovery failed')); + + // Access the private method + const getOrCreateConfig = async (provider: OidcProvider) => { + return (service as any).getOrCreateConfig(provider); + }; + + const config = await getOrCreateConfig(provider); + + // Verify configuration was created successfully for public client + expect(config).toBeDefined(); + expect(config.clientMetadata().client_secret).toBeUndefined(); + }); + + it('should throw error when discovery fails and no manual endpoints provided', async () => { + const provider: OidcProvider = { + id: 'no-manual-endpoints', + name: 'No Manual Endpoints', + clientId: 'test-client-id', + issuer: 'https://broken.example.com', + // Missing authorizationEndpoint and tokenEndpoint + scopes: ['openid', 'profile'], + authorizationRules: [], + }; + + oidcConfig.getProvider.mockResolvedValue(provider); + + // Mock discovery to fail + validationService.performDiscovery = vi + .fn() + .mockRejectedValue(new Error('Discovery failed')); + + // Access the private method + const getOrCreateConfig = async (provider: OidcProvider) => { + return (service as any).getOrCreateConfig(provider); + }; + + await expect(getOrCreateConfig(provider)).rejects.toThrow(UnauthorizedException); + }); + + it('should throw error when only authorization endpoint is provided', async () => { + const provider: OidcProvider = { + id: 'partial-manual-endpoints', + name: 'Partial Manual Endpoints', + clientId: 'test-client-id', + issuer: 'https://broken.example.com', + authorizationEndpoint: 'https://manual.example.com/auth', + // Missing tokenEndpoint + scopes: ['openid', 'profile'], + authorizationRules: [], + }; + + oidcConfig.getProvider.mockResolvedValue(provider); + + // Mock discovery to fail + validationService.performDiscovery = vi + .fn() + .mockRejectedValue(new Error('Discovery failed')); + + // Access the private method + const getOrCreateConfig = async (provider: OidcProvider) => { + return (service as any).getOrCreateConfig(provider); + }; + + await expect(getOrCreateConfig(provider)).rejects.toThrow(UnauthorizedException); + }); + + it('should cache manual configuration properly', async () => { + const provider: OidcProvider = { + id: 'cache-test', + name: 'Cache Test', + clientId: 'test-client-id', + clientSecret: 'test-secret', + issuer: 'https://manual.example.com', + authorizationEndpoint: 'https://manual.example.com/auth', + tokenEndpoint: 'https://manual.example.com/token', + scopes: ['openid', 'profile'], + authorizationRules: [], + }; + + oidcConfig.getProvider.mockResolvedValue(provider); + + // Mock discovery to fail + validationService.performDiscovery = vi + .fn() + .mockRejectedValue(new Error('Discovery failed')); + + // Access the private method + const getOrCreateConfig = async (provider: OidcProvider) => { + return (service as any).getOrCreateConfig(provider); + }; + + // First call should create configuration + const config1 = await getOrCreateConfig(provider); + + // Second call should return cached configuration + const config2 = await getOrCreateConfig(provider); + + expect(config1).toBe(config2); // Should be the exact same instance + expect(validationService.performDiscovery).toHaveBeenCalledTimes(1); // Only called once due to caching + }); + + it('should handle HTTP endpoints with allowInsecureRequests', async () => { + const provider: OidcProvider = { + id: 'http-endpoints', + name: 'HTTP Endpoints', + clientId: 'test-client-id', + clientSecret: 'test-secret', + issuer: 'http://manual.example.com', // HTTP instead of HTTPS + authorizationEndpoint: 'http://manual.example.com/auth', + tokenEndpoint: 'http://manual.example.com/token', + scopes: ['openid', 'profile'], + authorizationRules: [], + }; + + oidcConfig.getProvider.mockResolvedValue(provider); + + // Mock discovery to fail + validationService.performDiscovery = vi + .fn() + .mockRejectedValue(new Error('Discovery failed')); + + // Access the private method + const getOrCreateConfig = async (provider: OidcProvider) => { + return (service as any).getOrCreateConfig(provider); + }; + + const config = await getOrCreateConfig(provider); + + // Verify configuration was created successfully even with HTTP + expect(config).toBeDefined(); + expect(config.serverMetadata().token_endpoint).toBe('http://manual.example.com/token'); + expect(config.serverMetadata().authorization_endpoint).toBe( + 'http://manual.example.com/auth' + ); + }); + }); + + describe('getAuthorizationUrl', () => { + it('should generate authorization URL with custom authorization endpoint', async () => { + const provider: OidcProvider = { + id: 'test-provider', + name: 'Test Provider', + clientId: 'test-client-id', + issuer: 'https://example.com', + authorizationEndpoint: 'https://custom.example.com/auth', + scopes: ['openid', 'profile'], + authorizationRules: [], + }; + + oidcConfig.getProvider.mockResolvedValue(provider); + + const authUrl = await service.getAuthorizationUrl( + 'test-provider', + 'test-state', + 'localhost:3001' + ); + + expect(authUrl).toContain('https://custom.example.com/auth'); + expect(authUrl).toContain('client_id=test-client-id'); + expect(authUrl).toContain('response_type=code'); + expect(authUrl).toContain('scope=openid+profile'); + // State should start with provider ID followed by secure state token + expect(authUrl).toMatch(/state=test-provider%3A[a-f0-9]+\.[0-9]+\.[a-f0-9]+/); + expect(authUrl).toContain('redirect_uri='); + }); + + it('should encode provider ID in state parameter', async () => { + const provider: OidcProvider = { + id: 'encode-test-provider', + name: 'Encode Test Provider', + clientId: 'test-client-id', + issuer: 'https://example.com', + authorizationEndpoint: 'https://example.com/auth', + scopes: ['openid', 'email'], + authorizationRules: [], + }; + + oidcConfig.getProvider.mockResolvedValue(provider); + + const authUrl = await service.getAuthorizationUrl('encode-test-provider', 'original-state'); + + // Verify that the state parameter includes provider ID at the start + expect(authUrl).toMatch(/state=encode-test-provider%3A[a-f0-9]+\.[0-9]+\.[a-f0-9]+/); + }); + + it('should throw error when provider not found', async () => { + oidcConfig.getProvider.mockResolvedValue(null); + + await expect( + service.getAuthorizationUrl('nonexistent-provider', 'test-state') + ).rejects.toThrow('Provider nonexistent-provider not found'); + }); + + it('should handle custom scopes properly', async () => { + const provider: OidcProvider = { + id: 'custom-scopes-provider', + name: 'Custom Scopes Provider', + clientId: 'test-client-id', + issuer: 'https://example.com', + authorizationEndpoint: 'https://example.com/auth', + scopes: ['openid', 'profile', 'groups', 'custom:scope'], + authorizationRules: [], + }; + + oidcConfig.getProvider.mockResolvedValue(provider); + + const authUrl = await service.getAuthorizationUrl('custom-scopes-provider', 'test-state'); + + expect(authUrl).toContain('scope=openid+profile+groups+custom%3Ascope'); + }); + }); + + describe('handleCallback', () => { + it('should throw error when provider not found in callback', async () => { + oidcConfig.getProvider.mockResolvedValue(null); + + await expect( + service.handleCallback('nonexistent-provider', 'code', 'redirect-uri') + ).rejects.toThrow('Provider nonexistent-provider not found'); + }); + + it('should handle malformed state parameter', async () => { + await expect( + service.handleCallback('invalid-state', 'code', 'redirect-uri') + ).rejects.toThrow(UnauthorizedException); + }); + + it('should call getProvider with the provided provider ID', async () => { + const provider: OidcProvider = { + id: 'test-provider', + name: 'Test Provider', + clientId: 'test-client-id', + issuer: 'https://example.com', + scopes: ['openid'], + authorizationRules: [], + }; + + oidcConfig.getProvider.mockResolvedValue(provider); + + // This will fail during token exchange, but we're testing the provider lookup logic + await expect( + service.handleCallback('test-provider', 'code', 'redirect-uri') + ).rejects.toThrow(UnauthorizedException); + + // Verify the provider was looked up with the correct ID + expect(oidcConfig.getProvider).toHaveBeenCalledWith('test-provider'); + }); + }); + + describe('validateProvider', () => { + it('should delegate to validation service and return result', async () => { + const provider: OidcProvider = { + id: 'validate-provider', + name: 'Validate Provider', + clientId: 'test-client-id', + issuer: 'https://example.com', + scopes: ['openid'], + authorizationRules: [], + }; + + const expectedResult = { + isValid: true, + authorizationEndpoint: 'https://example.com/auth', + tokenEndpoint: 'https://example.com/token', + }; + + validationService.validateProvider.mockResolvedValue(expectedResult); + + const result = await service.validateProvider(provider); + + expect(result).toEqual(expectedResult); + expect(validationService.validateProvider).toHaveBeenCalledWith(provider); + }); + + it('should clear config cache before validation', async () => { + const provider: OidcProvider = { + id: 'cache-clear-provider', + name: 'Cache Clear Provider', + clientId: 'test-client-id', + issuer: 'https://example.com', + scopes: ['openid'], + authorizationRules: [], + }; + + const expectedResult = { + isValid: false, + error: 'Validation failed', + }; + + validationService.validateProvider.mockResolvedValue(expectedResult); + + const result = await service.validateProvider(provider); + + expect(result).toEqual(expectedResult); + // Verify the cache was cleared by checking the method was called + expect(validationService.validateProvider).toHaveBeenCalledWith(provider); + }); + }); + + describe('getRedirectUri (private method)', () => { + it('should generate correct redirect URI with localhost (development)', () => { + const getRedirectUri = (service as any).getRedirectUri.bind(service); + const redirectUri = getRedirectUri('localhost:3001'); + + expect(redirectUri).toBe('http://localhost:3000/graphql/api/auth/oidc/callback'); + }); + + it('should generate correct redirect URI with non-localhost host', () => { + const getRedirectUri = (service as any).getRedirectUri.bind(service); + + // Mock the ConfigService to return a production base URL + configService.get.mockReturnValue('https://example.com'); + + const redirectUri = getRedirectUri('example.com:443'); + + expect(redirectUri).toBe('https://example.com/graphql/api/auth/oidc/callback'); + }); + + it('should use default redirect URI when no request host provided', () => { + const getRedirectUri = (service as any).getRedirectUri.bind(service); + + // Mock the ConfigService to return a default value + configService.get.mockReturnValue('http://tower.local'); + + const redirectUri = getRedirectUri(); + + expect(redirectUri).toBe('http://tower.local/graphql/api/auth/oidc/callback'); + }); + }); +}); diff --git a/api/src/unraid-api/graph/resolvers/sso/oidc-auth.service.ts b/api/src/unraid-api/graph/resolvers/sso/oidc-auth.service.ts new file mode 100644 index 000000000..f0424c2b2 --- /dev/null +++ b/api/src/unraid-api/graph/resolvers/sso/oidc-auth.service.ts @@ -0,0 +1,667 @@ +import { Injectable, Logger, UnauthorizedException } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; + +import { decodeJwt } from 'jose'; +import * as client from 'openid-client'; + +import { OidcConfigPersistence } from '@app/unraid-api/graph/resolvers/sso/oidc-config.service.js'; +import { + AuthorizationOperator, + AuthorizationRuleMode, + OidcAuthorizationRule, + OidcProvider, +} from '@app/unraid-api/graph/resolvers/sso/oidc-provider.model.js'; +import { OidcSessionService } from '@app/unraid-api/graph/resolvers/sso/oidc-session.service.js'; +import { OidcStateService } from '@app/unraid-api/graph/resolvers/sso/oidc-state.service.js'; +import { OidcValidationService } from '@app/unraid-api/graph/resolvers/sso/oidc-validation.service.js'; + +interface JwtClaims { + sub?: string; + email?: string; + name?: string; + hd?: string; // Google hosted domain + [claim: string]: unknown; +} + +@Injectable() +export class OidcAuthService { + private readonly logger = new Logger(OidcAuthService.name); + private readonly configCache = new Map(); + + constructor( + private readonly configService: ConfigService, + private readonly oidcConfig: OidcConfigPersistence, + private readonly sessionService: OidcSessionService, + private readonly stateService: OidcStateService, + private readonly validationService: OidcValidationService + ) {} + + async getAuthorizationUrl(providerId: string, state: string, requestHost?: string): Promise { + const provider = await this.oidcConfig.getProvider(providerId); + if (!provider) { + throw new UnauthorizedException(`Provider ${providerId} not found`); + } + + const redirectUri = this.getRedirectUri(requestHost); + + // Generate secure state with cryptographic signature + const secureState = this.stateService.generateSecureState(providerId, state); + + // Build authorization URL + if (provider.authorizationEndpoint) { + // Use custom authorization endpoint + const authUrl = new URL(provider.authorizationEndpoint); + + // Standard OAuth2 parameters + authUrl.searchParams.set('client_id', provider.clientId); + authUrl.searchParams.set('redirect_uri', redirectUri); + authUrl.searchParams.set('scope', provider.scopes.join(' ')); + authUrl.searchParams.set('state', secureState); + authUrl.searchParams.set('response_type', 'code'); + + return authUrl.href; + } + + // Use OIDC discovery for providers without custom endpoints + const config = await this.getOrCreateConfig(provider); + const parameters: Record = { + redirect_uri: redirectUri, + scope: provider.scopes.join(' '), + state: secureState, + response_type: 'code', + }; + + // For HTTP endpoints, we need to pass the allowInsecureRequests option + const serverUrl = new URL(provider.issuer || ''); + let clientOptions: any = undefined; + if (serverUrl.protocol === 'http:') { + this.logger.debug( + `Building authorization URL with allowInsecureRequests for ${provider.id}` + ); + clientOptions = { + execute: [client.allowInsecureRequests], + }; + } + + const authUrl = client.buildAuthorizationUrl(config, parameters); + + return authUrl.href; + } + + extractProviderFromState(state: string): { providerId: string; originalState: string } { + // Extract provider from state prefix (no decryption needed) + const providerId = this.stateService.extractProviderFromState(state); + + if (providerId) { + return { + providerId, + originalState: state, + }; + } + + // Fallback for unknown formats + return { + providerId: '', + originalState: state, + }; + } + + async handleCallback( + providerId: string, + code: string, + state: string, + requestHost?: string, + fullCallbackUrl?: string + ): Promise { + const provider = await this.oidcConfig.getProvider(providerId); + if (!provider) { + throw new UnauthorizedException(`Provider ${providerId} not found`); + } + + try { + const redirectUri = this.getRedirectUri(requestHost); + + // Always use openid-client for consistency + const config = await this.getOrCreateConfig(provider); + + // Log configuration details + this.logger.debug(`Provider ${providerId} config loaded`); + this.logger.debug(`Redirect URI: ${redirectUri}`); + + // Build current URL for token exchange + // CRITICAL: The URL used here MUST match the redirect_uri that was sent to the authorization endpoint + // Google expects the exact same redirect_uri during token exchange + const currentUrl = new URL(redirectUri); + currentUrl.searchParams.set('code', code); + currentUrl.searchParams.set('state', state); + + // Copy additional parameters from the actual callback if provided + if (fullCallbackUrl) { + const actualUrl = new URL(fullCallbackUrl); + // Copy over additional params that Google might have added (scope, authuser, prompt, etc) + // but DO NOT change the base URL or path + ['scope', 'authuser', 'prompt', 'hd', 'session_state', 'iss'].forEach((param) => { + const value = actualUrl.searchParams.get(param); + if (value && !currentUrl.searchParams.has(param)) { + currentUrl.searchParams.set(param, value); + } + }); + } + + // Google returns iss in the response, openid-client v6 expects it + // If not present, add it based on the provider's issuer + if (!currentUrl.searchParams.has('iss') && provider.issuer) { + currentUrl.searchParams.set('iss', provider.issuer); + } + + this.logger.debug(`Token exchange URL (matches redirect_uri): ${currentUrl.href}`); + + // Validate secure state + const stateValidation = this.stateService.validateSecureState(state, providerId); + if (!stateValidation.isValid) { + this.logger.error(`State validation failed: ${stateValidation.error}`); + throw new UnauthorizedException(stateValidation.error || 'Invalid state parameter'); + } + + const originalState = stateValidation.clientState!; + this.logger.debug(`Exchanging code for tokens with provider ${providerId}`); + this.logger.debug(`Client state extracted: ${originalState}`); + + // For openid-client v6, we need to prepare the authorization response + const authorizationResponse = new URLSearchParams(currentUrl.search); + + // Set the original client state for openid-client + authorizationResponse.set('state', originalState); + + // Create a new URL with the cleaned parameters + const cleanUrl = new URL(redirectUri); + cleanUrl.search = authorizationResponse.toString(); + + this.logger.debug(`Clean URL for token exchange: ${cleanUrl.href}`); + + let tokens; + try { + this.logger.debug(`Starting token exchange with openid-client`); + this.logger.debug(`Config issuer: ${config.serverMetadata().issuer}`); + this.logger.debug(`Config token endpoint: ${config.serverMetadata().token_endpoint}`); + + // For HTTP endpoints, we need to pass the allowInsecureRequests option + const serverUrl = new URL(provider.issuer || ''); + let clientOptions: any = undefined; + if (serverUrl.protocol === 'http:') { + this.logger.debug(`Token exchange with allowInsecureRequests for ${provider.id}`); + clientOptions = { + execute: [client.allowInsecureRequests], + }; + } + + tokens = await client.authorizationCodeGrant( + config, + cleanUrl, + { + expectedState: originalState, + }, + clientOptions + ); + this.logger.debug( + `Token exchange successful, received tokens: ${Object.keys(tokens).join(', ')}` + ); + } catch (tokenError) { + const errorMessage = + tokenError instanceof Error ? tokenError.message : String(tokenError); + this.logger.error(`Token exchange failed: ${errorMessage}`); + + // Check if error message contains the "unexpected JWT claim" text + if (errorMessage.includes('unexpected JWT claim value encountered')) { + this.logger.error( + `unexpected JWT claim value encountered during token validation by openid-client` + ); + this.logger.debug( + `Token exchange error details: ${JSON.stringify(tokenError, null, 2)}` + ); + + // Log the actual vs expected issuer + this.logger.error( + `This error typically means the 'iss' claim in the JWT doesn't match the expected issuer` + ); + this.logger.error(`Check that your provider's issuer URL is configured correctly`); + } + + throw tokenError; + } + + // Parse ID token to get user info + let claims: JwtClaims | null = null; + if (tokens.id_token) { + try { + // Use jose to properly decode the JWT + claims = decodeJwt(tokens.id_token) as JwtClaims; + + // Log claims safely without PII - only structure, not values + if (claims) { + const claimKeys = Object.keys(claims).join(', '); + this.logger.debug( + `ID token decoded successfully. Available claims: [${claimKeys}]` + ); + + // Log claim types without exposing sensitive values + for (const [key, value] of Object.entries(claims)) { + const valueType = Array.isArray(value) + ? `array[${value.length}]` + : typeof value; + + // Only log structure, not actual values (avoid PII) + this.logger.debug(`Claim '${key}': type=${valueType}`); + + // Check for unexpected claim types + if (valueType === 'object' && value !== null && !Array.isArray(value)) { + this.logger.warn(`Claim '${key}' contains complex object structure`); + } + } + } + } catch (e) { + this.logger.warn(`Failed to parse ID token: ${e}`); + } + } else { + this.logger.error('No ID token received from provider'); + } + + if (!claims?.sub) { + this.logger.error( + 'No subject in token - claims available: ' + + (claims ? Object.keys(claims).join(', ') : 'none') + ); + throw new UnauthorizedException('No subject in token'); + } + + const userSub = claims.sub; + this.logger.debug(`Processing authentication for user: ${userSub}`); + + // Check authorization based on rules + // This will throw a helpful error if misconfigured or unauthorized + await this.checkAuthorization(provider, claims); + + // Create session and return padded token + const paddedToken = await this.sessionService.createSession(providerId, userSub); + + this.logger.log(`Successfully authenticated user ${userSub} via provider ${providerId}`); + + return paddedToken; + } catch (error) { + this.logger.error( + `OAuth callback error: ${error instanceof Error ? error.message : 'Unknown error'}` + ); + // Re-throw the original error if it's already an UnauthorizedException + if (error instanceof UnauthorizedException) { + throw error; + } + // Otherwise throw a generic error + throw new UnauthorizedException('Authentication failed'); + } + } + + private async getOrCreateConfig(provider: OidcProvider): Promise { + const cacheKey = provider.id; + + if (this.configCache.has(cacheKey)) { + return this.configCache.get(cacheKey)!; + } + + try { + // Use the validation service to perform discovery with HTTP support + if (provider.issuer) { + this.logger.debug(`Attempting discovery for ${provider.id} at ${provider.issuer}`); + + // Create client options with HTTP support if needed + const serverUrl = new URL(provider.issuer); + let clientOptions: any = undefined; + if (serverUrl.protocol === 'http:') { + this.logger.debug(`Allowing HTTP for ${provider.id} as specified by user`); + clientOptions = { + execute: [client.allowInsecureRequests], + }; + } + + try { + const config = await this.validationService.performDiscovery( + provider, + clientOptions + ); + this.logger.debug(`Discovery successful for ${provider.id}`); + this.logger.debug( + `Authorization endpoint: ${config.serverMetadata().authorization_endpoint}` + ); + this.logger.debug(`Token endpoint: ${config.serverMetadata().token_endpoint}`); + this.configCache.set(cacheKey, config); + return config; + } catch (discoveryError) { + const errorMessage = + discoveryError instanceof Error ? discoveryError.message : 'Unknown error'; + this.logger.warn(`Discovery failed for ${provider.id}: ${errorMessage}`); + + // Log more details about the discovery error + this.logger.debug( + `Discovery URL attempted: ${provider.issuer}/.well-known/openid-configuration` + ); + this.logger.debug( + `Full discovery error: ${JSON.stringify(discoveryError, null, 2)}` + ); + + // Log stack trace for better debugging + if (discoveryError instanceof Error && discoveryError.stack) { + this.logger.debug(`Stack trace: ${discoveryError.stack}`); + } + + // If discovery fails but we have manual endpoints, use them + if (provider.authorizationEndpoint && provider.tokenEndpoint) { + this.logger.log(`Using manual endpoints for ${provider.id}`); + + // Create manual configuration + const serverMetadata: client.ServerMetadata = { + issuer: provider.issuer || `manual-${provider.id}`, + authorization_endpoint: provider.authorizationEndpoint, + token_endpoint: provider.tokenEndpoint, + jwks_uri: provider.jwksUri, + }; + + const clientMetadata: Partial = { + client_secret: provider.clientSecret, + }; + + // Configure client auth method + const clientAuth = provider.clientSecret + ? client.ClientSecretPost(provider.clientSecret) + : client.None(); + + try { + const config = new client.Configuration( + serverMetadata, + provider.clientId, + clientMetadata, + clientAuth + ); + + // Use manual configuration with HTTP support if needed + const serverUrl = new URL(provider.tokenEndpoint); + if (serverUrl.protocol === 'http:') { + this.logger.debug( + `Allowing HTTP for manual endpoints on ${provider.id}` + ); + client.allowInsecureRequests(config); + } + + this.logger.debug(`Manual configuration created for ${provider.id}`); + this.logger.debug( + `Authorization endpoint: ${serverMetadata.authorization_endpoint}` + ); + this.logger.debug(`Token endpoint: ${serverMetadata.token_endpoint}`); + + this.configCache.set(cacheKey, config); + return config; + } catch (manualConfigError) { + this.logger.error( + `Failed to create manual configuration: ${manualConfigError instanceof Error ? manualConfigError.message : 'Unknown error'}` + ); + throw new Error(`Manual configuration failed for ${provider.id}`); + } + } else { + throw new Error( + `OIDC discovery failed and no manual endpoints provided for ${provider.id}` + ); + } + } + } + + // Manual configuration when no issuer is provided + if (provider.authorizationEndpoint && provider.tokenEndpoint) { + this.logger.log(`Using manual endpoints for ${provider.id} (no issuer provided)`); + + // Create manual configuration + const serverMetadata: client.ServerMetadata = { + issuer: provider.issuer || `manual-${provider.id}`, + authorization_endpoint: provider.authorizationEndpoint, + token_endpoint: provider.tokenEndpoint, + jwks_uri: provider.jwksUri, + }; + + const clientMetadata: Partial = { + client_secret: provider.clientSecret, + }; + + // Configure client auth method + const clientAuth = provider.clientSecret + ? client.ClientSecretPost(provider.clientSecret) + : client.None(); + + try { + const config = new client.Configuration( + serverMetadata, + provider.clientId, + clientMetadata, + clientAuth + ); + + // Use manual configuration with HTTP support if needed + const serverUrl = new URL(provider.tokenEndpoint); + if (serverUrl.protocol === 'http:') { + this.logger.debug(`Allowing HTTP for manual endpoints on ${provider.id}`); + client.allowInsecureRequests(config); + } + + this.logger.debug(`Manual configuration created for ${provider.id}`); + this.logger.debug( + `Authorization endpoint: ${serverMetadata.authorization_endpoint}` + ); + this.logger.debug(`Token endpoint: ${serverMetadata.token_endpoint}`); + + this.configCache.set(cacheKey, config); + return config; + } catch (manualConfigError) { + this.logger.error( + `Failed to create manual configuration: ${manualConfigError instanceof Error ? manualConfigError.message : 'Unknown error'}` + ); + throw new Error(`Manual configuration failed for ${provider.id}`); + } + } + + // If we reach here, neither discovery nor manual endpoints are available + throw new Error( + `No configuration method available for ${provider.id}: requires either valid issuer for discovery or manual endpoints` + ); + } catch (error) { + this.logger.error( + `Failed to create OIDC configuration for ${provider.id}: ${ + error instanceof Error ? error.message : 'Unknown error' + }` + ); + + // Log more details in debug mode + if (error instanceof Error && error.stack) { + this.logger.debug(`Stack trace: ${error.stack}`); + } + + throw new UnauthorizedException('Provider configuration error'); + } + } + + private async checkAuthorization(provider: OidcProvider, claims: JwtClaims): Promise { + this.logger.debug( + `Checking authorization for provider ${provider.id} with ${provider.authorizationRules?.length || 0} rules` + ); + this.logger.debug(`Available claims: ${Object.keys(claims).join(', ')}`); + this.logger.debug( + `Authorization rule mode: ${provider.authorizationRuleMode || AuthorizationRuleMode.OR}` + ); + + // If no authorization rules are specified, throw a helpful error + if (!provider.authorizationRules || provider.authorizationRules.length === 0) { + throw new UnauthorizedException( + `Login failed: The ${provider.name} provider has no authorization rules configured. ` + + `Please configure authorization rules.` + ); + } + + this.logger.debug( + `Authorization rules to evaluate: ${JSON.stringify(provider.authorizationRules, null, 2)}` + ); + + // Evaluate the rules + const ruleMode = provider.authorizationRuleMode || AuthorizationRuleMode.OR; + const isAuthorized = this.evaluateAuthorizationRules( + provider.authorizationRules, + claims, + ruleMode + ); + + this.logger.debug(`Authorization result: ${isAuthorized}`); + + if (!isAuthorized) { + // Log authorization failure with safe claim representation (no PII) + const availableClaimKeys = Object.keys(claims).join(', '); + this.logger.warn( + `Authorization failed for provider ${provider.name}, user ${claims.sub}, available claim keys: [${availableClaimKeys}]` + ); + throw new UnauthorizedException( + `Access denied: Your account does not meet the authorization requirements for ${provider.name}.` + ); + } + + this.logger.debug(`Authorization successful for user ${claims.sub}`); + } + + private evaluateAuthorizationRules( + rules: OidcAuthorizationRule[], + claims: JwtClaims, + mode: AuthorizationRuleMode = AuthorizationRuleMode.OR + ): boolean { + // No rules means no authorization + if (rules.length === 0) { + return false; + } + + if (mode === AuthorizationRuleMode.AND) { + // All rules must pass (AND logic) + return rules.every((rule) => this.evaluateRule(rule, claims)); + } else { + // Any rule can pass (OR logic) - default behavior + // Multiple rules act as alternative authorization paths + return rules.some((rule) => this.evaluateRule(rule, claims)); + } + } + + private evaluateRule(rule: OidcAuthorizationRule, claims: JwtClaims): boolean { + const claimValue = claims[rule.claim]; + + this.logger.debug( + `Evaluating rule for claim ${rule.claim}: ${JSON.stringify({ + claimValue, + claimType: typeof claimValue, + ruleOperator: rule.operator, + ruleValues: rule.value, + })}` + ); + + if (claimValue === undefined || claimValue === null) { + this.logger.debug(`Claim ${rule.claim} not found in token`); + return false; + } + + // Log detailed claim analysis + if (typeof claimValue === 'object' && claimValue !== null) { + this.logger.warn( + `unexpected JWT claim value encountered - claim ${rule.claim} is object type: ${JSON.stringify(claimValue)}` + ); + return false; + } + + if (Array.isArray(claimValue)) { + this.logger.warn( + `unexpected JWT claim value encountered - claim ${rule.claim} is array type: ${JSON.stringify(claimValue)}` + ); + return false; + } + + const value = String(claimValue); + this.logger.debug(`Processing claim ${rule.claim} with string value: "${value}"`); + + let result: boolean; + switch (rule.operator) { + case AuthorizationOperator.EQUALS: + result = rule.value.some((v) => value === v); + this.logger.debug( + `EQUALS check: "${value}" matches any of [${rule.value.join(', ')}]: ${result}` + ); + return result; + + case AuthorizationOperator.CONTAINS: + result = rule.value.some((v) => value.includes(v)); + this.logger.debug( + `CONTAINS check: "${value}" contains any of [${rule.value.join(', ')}]: ${result}` + ); + return result; + + case AuthorizationOperator.STARTS_WITH: + result = rule.value.some((v) => value.startsWith(v)); + this.logger.debug( + `STARTS_WITH check: "${value}" starts with any of [${rule.value.join(', ')}]: ${result}` + ); + return result; + + case AuthorizationOperator.ENDS_WITH: + result = rule.value.some((v) => value.endsWith(v)); + this.logger.debug( + `ENDS_WITH check: "${value}" ends with any of [${rule.value.join(', ')}]: ${result}` + ); + return result; + + default: + this.logger.error(`Unknown authorization operator: ${rule.operator}`); + return false; + } + } + + /** + * Validate OIDC provider configuration by attempting discovery + * Returns validation result with helpful error messages for debugging + */ + async validateProvider( + provider: OidcProvider + ): Promise<{ isValid: boolean; error?: string; details?: unknown }> { + // Clear any cached config for this provider to force fresh validation + this.configCache.delete(provider.id); + + // Delegate to the validation service + return this.validationService.validateProvider(provider); + } + + private getRedirectUri(requestHost?: string): string { + // Always use the proxied path through /graphql to match production + if (requestHost && requestHost.includes('localhost')) { + // In development, use the Nuxt proxy at port 3000 + return `http://localhost:3000/graphql/api/auth/oidc/callback`; + } + + // In production, use the actual request host or configured base URL + if (requestHost) { + // Parse the host to handle port numbers properly + const isLocalhost = requestHost.includes('localhost'); + const protocol = isLocalhost ? 'http' : 'https'; + + // Remove standard ports (:443 for HTTPS, :80 for HTTP) + let cleanHost = requestHost; + if (!isLocalhost) { + if (requestHost.endsWith(':443')) { + cleanHost = requestHost.slice(0, -4); // Remove :443 + } else if (requestHost.endsWith(':80')) { + cleanHost = requestHost.slice(0, -3); // Remove :80 + } + } + + return `${protocol}://${cleanHost}/graphql/api/auth/oidc/callback`; + } + + // Fall back to configured BASE_URL or default + const baseUrl = this.configService.get('BASE_URL', 'http://tower.local'); + return `${baseUrl}/graphql/api/auth/oidc/callback`; + } +} diff --git a/api/src/unraid-api/graph/resolvers/sso/oidc-config.service.ts b/api/src/unraid-api/graph/resolvers/sso/oidc-config.service.ts new file mode 100644 index 000000000..d4ffee178 --- /dev/null +++ b/api/src/unraid-api/graph/resolvers/sso/oidc-config.service.ts @@ -0,0 +1,1098 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; + +import { RuleEffect } from '@jsonforms/core'; +import { mergeSettingSlices } from '@unraid/shared/jsonforms/settings.js'; +import { ConfigFilePersister } from '@unraid/shared/services/config-file.js'; +import { UserSettingsService } from '@unraid/shared/services/user-settings.js'; + +import { + AuthorizationOperator, + OidcAuthorizationRule, + OidcProvider, +} from '@app/unraid-api/graph/resolvers/sso/oidc-provider.model.js'; +import { OidcValidationService } from '@app/unraid-api/graph/resolvers/sso/oidc-validation.service.js'; +import { + createAccordionLayout, + createLabeledControl, + createSimpleLabeledControl, +} from '@app/unraid-api/graph/utils/form-utils.js'; +import { SettingSlice } from '@app/unraid-api/types/json-forms.js'; + +export interface OidcConfig { + providers: OidcProvider[]; +} + +@Injectable() +export class OidcConfigPersistence extends ConfigFilePersister { + constructor( + configService: ConfigService, + private readonly userSettings: UserSettingsService, + private readonly validationService: OidcValidationService + ) { + super(configService); + this.registerSettings(); + } + + fileName(): string { + // Check for environment variable override + const envPath = process.env.PATHS_OIDC_JSON; + if (envPath) { + // Extract just the filename from the path + const parts = envPath.split('/'); + return parts[parts.length - 1]; + } + return 'oidc.json'; + } + + configKey(): string { + return 'oidc'; + } + + defaultConfig(): OidcConfig { + return { + providers: [this.getUnraidNetSsoProvider()], + }; + } + + private getUnraidNetSsoProvider(): OidcProvider { + return { + id: 'unraid.net', + name: 'Unraid.net', + clientId: 'CONNECT_SERVER_SSO', + issuer: 'https://account.unraid.net', + scopes: ['openid', 'profile', 'email'], + authorizationRules: [], + buttonText: 'Login With Unraid.net', + buttonIcon: + 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMzMuNTIgNzYuOTciPjx0aXRsZT5VTi1tYXJrLXdoaXRlPC90aXRsZT48cGF0aCBkPSJNNjMuNDksMTkuMjRINzBWNTcuNzNINjMuNDlaTTYuNTQsNTcuNzNIMFYxOS4yNEg2LjU0Wm0yNS4yLDQuNTRoNi41NVY3N0gzMS43NFpNMTUuODcsNDUuODRoNi41NFY2OS42MkgxNS44N1ptMzEuNzUsMGg2LjU0VjY5LjYySDQ3LjYyWk0xMjcsMTkuMjRoNi41NFY1Ny43M0gxMjdaTTEwMS43NywxNC43SDk1LjIzVjBoNi41NFptMTUuODgsMTYuNDRIMTExLjFWNy4zNWg2LjU1Wm0tMzEuNzUsMEg3OS4zNlY3LjM1SDg1LjlaIiBmaWxsPSIjZmZmIi8+PC9zdmc+', + buttonVariant: 'primary', + buttonStyle: + 'background-color: #ff6600; border-color: #ff6600; color: white; transition: all 0.2s;', + }; + } + + async migrateConfig(): Promise { + // Get existing SSO users from the main config + const ssoSubIds = this.configService.get('api.ssoSubIds', []); + + // Always ensure Unraid.net SSO provider is present with migrated users + const unraidNetSsoProvider = this.getUnraidNetSsoProvider(); + + // Convert legacy authorizedSubIds to authorization rules + if (ssoSubIds.length > 0) { + unraidNetSsoProvider.authorizationRules = [ + { + claim: 'sub', + operator: AuthorizationOperator.EQUALS, + value: ssoSubIds, + }, + ]; + this.logger.log(`Migrated ${ssoSubIds.length} SSO users to authorization rules`); + } + + return { + providers: [unraidNetSsoProvider], + }; + } + + async getProviders(): Promise { + const config = this.configService.get(this.configKey()); + const providers = config?.providers || []; + + // Ensure unraid.net provider is always present with current defaults + const hasUnraidNet = providers.some((p) => p.id === 'unraid.net'); + if (!hasUnraidNet) { + providers.unshift(this.getUnraidNetSsoProvider()); + } + + // Ensure unraid.net provider always has current defaults while preserving authorization rules + const updatedProviders = providers.map((provider) => { + if (provider.id === 'unraid.net') { + const currentDefaults = this.getUnraidNetSsoProvider(); + // Preserve existing authorization rules but override UI/button properties + return { + ...provider, + ...currentDefaults, + // Keep existing authorization rules if they exist + authorizationRules: + provider.authorizationRules || currentDefaults.authorizationRules, + }; + } + return provider; + }); + + // Clean up providers - convert empty strings to undefined + return updatedProviders.map((provider) => this.cleanProvider(provider)); + } + + private cleanProvider(provider: OidcProvider): OidcProvider { + // Convert empty strings to undefined for optional fields + return { + ...provider, + clientSecret: provider.clientSecret?.trim() || undefined, + authorizationEndpoint: provider.authorizationEndpoint?.trim() || undefined, + tokenEndpoint: provider.tokenEndpoint?.trim() || undefined, + buttonIcon: provider.buttonIcon?.trim() || undefined, + buttonStyle: provider.buttonStyle?.trim() || undefined, + }; + } + + async getProvider(id: string): Promise { + const providers = await this.getProviders(); + return providers.find((p) => p.id === id) || null; + } + + async upsertProvider( + provider: OidcProvider & { authorizationMode?: string; simpleAuthorization?: any } + ): Promise { + const config = this.configService.get(this.configKey()) || this.defaultConfig(); + const providers = [...config.providers]; + + // If in simple mode, convert simple fields to authorization rules + if (provider.authorizationMode === 'simple' && provider.simpleAuthorization) { + const rules = this.convertSimpleToRules(provider.simpleAuthorization); + provider.authorizationRules = rules; + } + + // Clean up the provider object - remove UI-only fields + const cleanedProvider: OidcProvider = { + id: provider.id, + name: provider.name, + clientId: provider.clientId, + clientSecret: provider.clientSecret, + issuer: provider.issuer, + authorizationEndpoint: provider.authorizationEndpoint, + tokenEndpoint: provider.tokenEndpoint, + jwksUri: provider.jwksUri, + scopes: provider.scopes, + authorizationRules: provider.authorizationRules, + buttonText: provider.buttonText, + buttonIcon: provider.buttonIcon, + buttonVariant: provider.buttonVariant, + buttonStyle: provider.buttonStyle, + }; + + const existingIndex = providers.findIndex((p) => p.id === provider.id); + if (existingIndex >= 0) { + providers[existingIndex] = cleanedProvider; + } else { + providers.push(cleanedProvider); + } + + const newConfig = { ...config, providers }; + this.configService.set(this.configKey(), newConfig); + await this.persist(newConfig); + + return cleanedProvider; + } + + private convertSimpleToRules(simpleAuth: { + allowedDomains?: string[]; + allowedEmails?: string[]; + allowedUserIds?: string[]; + googleWorkspaceDomain?: string; + }): OidcAuthorizationRule[] { + const rules: OidcAuthorizationRule[] = []; + + // Convert email domains to endsWith rules + if (simpleAuth?.allowedDomains && simpleAuth.allowedDomains.length > 0) { + rules.push({ + claim: 'email', + operator: AuthorizationOperator.ENDS_WITH, + value: simpleAuth.allowedDomains.map((domain: string) => + domain.startsWith('@') ? domain : `@${domain}` + ), + }); + } + + // Convert specific emails to equals rules + if (simpleAuth?.allowedEmails && simpleAuth.allowedEmails.length > 0) { + rules.push({ + claim: 'email', + operator: AuthorizationOperator.EQUALS, + value: simpleAuth.allowedEmails, + }); + } + + // Convert user IDs to sub equals rules + if (simpleAuth?.allowedUserIds && simpleAuth.allowedUserIds.length > 0) { + rules.push({ + claim: 'sub', + operator: AuthorizationOperator.EQUALS, + value: simpleAuth.allowedUserIds, + }); + } + + // Google Workspace domain (hd claim) + if (simpleAuth?.googleWorkspaceDomain) { + rules.push({ + claim: 'hd', + operator: AuthorizationOperator.EQUALS, + value: [simpleAuth.googleWorkspaceDomain], + }); + } + + return rules; + } + + async deleteProvider(id: string): Promise { + // Prevent deletion of the unraid.net provider + if (id === 'unraid.net') { + this.logger.warn(`Attempted to delete protected provider: ${id}`); + return false; + } + + const config = this.configService.get(this.configKey()) || this.defaultConfig(); + const filteredProviders = config.providers.filter((p) => p.id !== id); + + if (filteredProviders.length === config.providers.length) { + return false; + } + + const newConfig = { ...config, providers: filteredProviders }; + this.configService.set(this.configKey(), newConfig); + await this.persist(newConfig); + + return true; + } + + private registerSettings() { + this.userSettings.register('sso', { + buildSlice: async () => this.buildSlice(), + getCurrentValues: async () => this.getConfig(), + updateValues: async ( + config: OidcConfig & { + providers: Array< + OidcProvider & { authorizationMode?: string; simpleAuthorization?: unknown } + >; + } + ) => { + // Process each provider to handle simple mode conversion + const processedConfig: OidcConfig = { + ...config, + providers: config.providers.map((provider) => { + const extendedProvider = provider as OidcProvider & { + authorizationMode?: string; + simpleAuthorization?: unknown; + }; + // If in simple mode, convert simple fields to authorization rules + if ( + extendedProvider.authorizationMode === 'simple' && + extendedProvider.simpleAuthorization + ) { + const rules = this.convertSimpleToRules( + extendedProvider.simpleAuthorization as { + allowedDomains?: string[]; + allowedEmails?: string[]; + allowedUserIds?: string[]; + googleWorkspaceDomain?: string; + } + ); + // Return provider with generated rules, removing UI-only fields + const { authorizationMode, simpleAuthorization, ...cleanProvider } = + extendedProvider; + return { + ...cleanProvider, + authorizationRules: rules, + }; + } + // If in advanced mode or no mode specified, just clean up UI fields + const { authorizationMode, simpleAuthorization, ...cleanProvider } = + extendedProvider; + return cleanProvider; + }), + }; + + // Validate OIDC discovery for all providers with issuer URLs + const validationErrors: string[] = []; + for (const provider of processedConfig.providers) { + if (provider.issuer) { + try { + // Parse the issuer URL and check if hostname is exactly 'unraid.net' + const issuerUrl = new URL(provider.issuer); + if (issuerUrl.hostname === 'unraid.net') { + // Skip validation for unraid.net as it uses custom auth flow + continue; + } + } catch (urlError) { + // Invalid URL, proceed with validation + } + + try { + const validation = await this.validationService.validateProvider(provider); + if (!validation.isValid) { + validationErrors.push(`❌ ${provider.name}: ${validation.error}`); + } + } catch (error) { + // Don't fail the save, just warn + this.logger.warn(`Failed to validate provider ${provider.id}: ${error}`); + } + } + } + + this.configService.set(this.configKey(), processedConfig); + await this.persist(processedConfig); + + // Include validation results in response + const response: { restartRequired: boolean; values: OidcConfig; warnings?: string[] } = { + restartRequired: false, + values: processedConfig, + }; + + if (validationErrors.length > 0) { + response.warnings = [ + '⚠️ OIDC Discovery Issues Found:', + '', + ...validationErrors, + '', + '💡 These providers may not work properly. Please check your configuration.', + 'Note: Configuration has been saved, but you should fix these issues before testing login.', + ]; + } + + return response; + }, + }); + } + + getConfig(): OidcConfig & { + providers: Array< + OidcProvider & { + authorizationMode?: string; + simpleAuthorization?: unknown; + isProtected?: boolean; + } + >; + } { + const config = this.configService.get(this.configKey()) || this.defaultConfig(); + + // Ensure unraid.net provider always has current defaults while preserving authorization rules + const providers = config.providers.map((provider) => { + if (provider.id === 'unraid.net') { + const currentDefaults = this.getUnraidNetSsoProvider(); + // Preserve existing authorization rules but override UI/button properties + return { + ...provider, + ...currentDefaults, + // Keep existing authorization rules if they exist + authorizationRules: + provider.authorizationRules || currentDefaults.authorizationRules, + }; + } + return provider; + }); + + // Enhance providers with UI fields + const enhancedProviders = providers.map((provider) => { + const simpleAuth = this.convertRulesToSimple(provider.authorizationRules || []); + + // Determine if rules can be represented in simple mode + const canUseSimpleMode = this.canConvertToSimpleMode(provider.authorizationRules || []); + + return { + ...provider, + authorizationMode: canUseSimpleMode ? 'simple' : 'advanced', + simpleAuthorization: simpleAuth, + isProtected: provider.id === 'unraid.net', // Mark unraid.net as protected + }; + }); + + return { + ...config, + providers: enhancedProviders, + }; + } + + private canConvertToSimpleMode(rules: OidcAuthorizationRule[]): boolean { + // Check if all rules match simple patterns + return rules.every((rule) => { + // Email domain rules + if (rule.claim === 'email' && rule.operator === AuthorizationOperator.ENDS_WITH) { + return rule.value.every((v) => v.startsWith('@')); + } + // Email equals rules + if (rule.claim === 'email' && rule.operator === AuthorizationOperator.EQUALS) { + return true; + } + // Sub equals rules + if (rule.claim === 'sub' && rule.operator === AuthorizationOperator.EQUALS) { + return true; + } + // Google Workspace domain + if (rule.claim === 'hd' && rule.operator === AuthorizationOperator.EQUALS) { + return true; + } + return false; + }); + } + + private convertRulesToSimple(rules: OidcAuthorizationRule[]): { + allowedDomains: string[]; + allowedEmails: string[]; + allowedUserIds: string[]; + googleWorkspaceDomain?: string; + } { + const simpleAuth = { + allowedDomains: [] as string[], + allowedEmails: [] as string[], + allowedUserIds: [] as string[], + googleWorkspaceDomain: undefined as string | undefined, + }; + + rules.forEach((rule) => { + if (rule.claim === 'email' && rule.operator === AuthorizationOperator.ENDS_WITH) { + simpleAuth.allowedDomains = rule.value.map((v) => + v.startsWith('@') ? v.substring(1) : v + ); + } else if (rule.claim === 'email' && rule.operator === AuthorizationOperator.EQUALS) { + simpleAuth.allowedEmails = rule.value; + } else if (rule.claim === 'sub' && rule.operator === AuthorizationOperator.EQUALS) { + simpleAuth.allowedUserIds = rule.value; + } else if ( + rule.claim === 'hd' && + rule.operator === AuthorizationOperator.EQUALS && + rule.value.length > 0 + ) { + simpleAuth.googleWorkspaceDomain = rule.value[0]; + } + }); + + return simpleAuth; + } + + private buildSlice(): SettingSlice { + return mergeSettingSlices([this.oidcProvidersSlice()], { as: 'sso' }); + } + + private oidcProvidersSlice(): SettingSlice { + return { + properties: { + providers: { + type: 'array', + items: { + type: 'object', + properties: { + id: { + type: 'string', + title: 'Provider ID', + description: 'Unique identifier for the provider', + pattern: '^[a-zA-Z0-9._-]+$', + }, + name: { + type: 'string', + title: 'Provider Name', + description: 'Display name for the provider', + }, + clientId: { + type: 'string', + title: 'Client ID', + description: 'OAuth2 client ID registered with the provider', + }, + clientSecret: { + type: 'string', + title: 'Client Secret', + description: 'OAuth2 client secret (if required)', + }, + issuer: { + type: 'string', + title: 'Issuer URL', + format: 'uri', + description: 'OIDC issuer URL (e.g., https://accounts.google.com)', + }, + authorizationEndpoint: { + anyOf: [ + { type: 'string', minLength: 1, format: 'uri' }, + { type: 'string', maxLength: 0 }, + ], + title: 'Authorization Endpoint', + description: 'Optional - will be auto-discovered if not provided', + }, + tokenEndpoint: { + anyOf: [ + { type: 'string', minLength: 1, format: 'uri' }, + { type: 'string', maxLength: 0 }, + ], + title: 'Token Endpoint', + description: 'Optional - will be auto-discovered if not provided', + }, + jwksUri: { + anyOf: [ + { type: 'string', minLength: 1, format: 'uri' }, + { type: 'string', maxLength: 0 }, + ], + title: 'JWKS URI', + description: 'Optional - will be auto-discovered if not provided', + }, + scopes: { + type: 'array', + items: { type: 'string' }, + title: 'Scopes', + default: ['openid', 'profile', 'email'], + description: 'OAuth2 scopes to request', + }, + authorizationMode: { + type: 'string', + title: 'Authorization Mode', + enum: ['simple', 'advanced'], + default: 'simple', + description: + 'Choose between simple presets or advanced rule configuration', + }, + simpleAuthorization: { + type: 'object', + properties: { + allowedDomains: { + type: 'array', + items: { type: 'string' }, + title: 'Allowed Email Domains', + description: + 'Email domains that are allowed to login (e.g., company.com)', + }, + allowedEmails: { + type: 'array', + items: { type: 'string' }, + title: 'Specific Email Addresses', + description: + 'Specific email addresses that are allowed to login', + }, + allowedUserIds: { + type: 'array', + items: { type: 'string' }, + title: 'Allowed User IDs', + description: + 'Specific user IDs (sub claim) that are allowed to login', + }, + googleWorkspaceDomain: { + type: 'string', + title: 'Google Workspace Domain', + description: + 'Restrict to users from a specific Google Workspace domain', + }, + }, + }, + authorizationRuleMode: { + type: 'string', + title: 'Rule Mode', + enum: ['or', 'and'], + default: 'or', + description: + 'How to evaluate multiple rules: OR (any rule passes) or AND (all rules must pass)', + }, + authorizationRules: { + type: 'array', + items: { + type: 'object', + properties: { + claim: { + type: 'string', + title: 'Claim', + description: 'JWT claim to check', + }, + operator: { + type: 'string', + title: 'Operator', + enum: ['equals', 'contains', 'endsWith', 'startsWith'], + }, + value: { + type: 'array', + items: { type: 'string' }, + title: 'Values', + description: 'Values to match against', + }, + }, + required: ['claim', 'operator', 'value'], + }, + title: 'Claim Rules', + description: + 'Define authorization rules based on claims in the ID token. Rule mode can be configured: OR logic (any rule matches) or AND logic (all rules must match).', + }, + buttonText: { + type: 'string', + title: 'Button Text', + description: 'Custom text for the login button', + }, + buttonIcon: { + anyOf: [ + { type: 'string', minLength: 1 }, + { type: 'string', maxLength: 0 }, + ], + title: 'Button Icon URL', + description: 'URL or base64 encoded icon for the login button', + }, + buttonVariant: { + type: 'string', + title: 'Button Style', + enum: [ + 'primary', + 'destructive', + 'outline', + 'secondary', + 'ghost', + 'link', + ], + description: 'Visual style of the login button', + default: 'outline', + }, + buttonStyle: { + type: 'string', + title: 'Custom CSS Styles', + description: + 'Custom inline CSS styles for the button (e.g., "background: linear-gradient(to right, #4f46e5, #7c3aed); border-radius: 9999px;")', + }, + }, + required: ['id', 'name', 'clientId', 'issuer'], + }, + title: 'OIDC Providers', + description: 'Configure OpenID Connect providers for SSO authentication', + }, + }, + elements: [ + { + type: 'VerticalLayout', + elements: [ + { + type: 'Label', + text: 'OIDC Providers', + options: { + format: 'title', + }, + }, + { + type: 'Control', + scope: '#/properties/sso/properties/providers', + options: { + elementLabelProp: 'name', + itemTypeName: 'Provider', + protectedItems: [{ field: 'id', value: 'unraid.net' }], + itemWarnings: [ + { + condition: { field: 'id', value: 'unraid.net' }, + title: 'Unraid.net Provider', + description: + 'This is the built-in Unraid.net provider. Only authorization rules can be modified.', + }, + ], + detail: createAccordionLayout({ + defaultOpen: [0], + elements: [ + { + type: 'VerticalLayout', + options: { + accordion: { + title: 'Basic Configuration', + description: 'Essential provider settings', + }, + }, + rule: { + effect: RuleEffect.HIDE, + condition: { + scope: '#/properties/id', + schema: { const: 'unraid.net' }, + }, + }, + elements: [ + createSimpleLabeledControl({ + scope: '#/properties/id', + label: 'Provider ID:', + description: + 'Unique identifier (e.g., google, github)', + controlOptions: { + inputType: 'text', + placeholder: 'provider-id', + }, + rule: { + effect: RuleEffect.HIDE, + condition: { + scope: '#/properties/id', + schema: { const: 'unraid.net' }, + }, + }, + }), + createSimpleLabeledControl({ + scope: '#/properties/name', + label: 'Provider Name:', + description: 'Display name for users', + controlOptions: { + inputType: 'text', + placeholder: 'My Provider', + }, + rule: { + effect: RuleEffect.HIDE, + condition: { + scope: '#/properties/id', + schema: { const: 'unraid.net' }, + }, + }, + }), + createSimpleLabeledControl({ + scope: '#/properties/clientId', + label: 'Client ID:', + description: 'OAuth2 application client ID', + controlOptions: { + inputType: 'text', + }, + rule: { + effect: RuleEffect.HIDE, + condition: { + scope: '#/properties/id', + schema: { const: 'unraid.net' }, + }, + }, + }), + createSimpleLabeledControl({ + scope: '#/properties/clientSecret', + label: 'Client Secret:', + description: + 'OAuth2 application client secret (optional)', + controlOptions: { + inputType: 'password', + }, + rule: { + effect: RuleEffect.HIDE, + condition: { + scope: '#/properties/id', + schema: { const: 'unraid.net' }, + }, + }, + }), + createSimpleLabeledControl({ + scope: '#/properties/issuer', + label: 'Issuer URL:', + description: 'OIDC issuer/discovery URL', + controlOptions: { + inputType: 'url', + placeholder: 'https://accounts.google.com', + }, + rule: { + effect: RuleEffect.HIDE, + condition: { + scope: '#/properties/id', + schema: { const: 'unraid.net' }, + }, + }, + }), + createSimpleLabeledControl({ + scope: '#/properties/scopes', + label: 'OAuth Scopes:', + description: 'Scopes to request from the provider', + controlOptions: { + format: 'array', + inputType: 'text', + placeholder: 'openid', + }, + rule: { + effect: RuleEffect.HIDE, + condition: { + scope: '#/properties/id', + schema: { const: 'unraid.net' }, + }, + }, + }), + ], + }, + { + type: 'VerticalLayout', + options: { + accordion: { + title: 'Advanced Endpoints', + description: + 'Override auto-discovery settings (optional)', + }, + }, + rule: { + effect: RuleEffect.HIDE, + condition: { + scope: '#/properties/id', + schema: { const: 'unraid.net' }, + }, + }, + elements: [ + createSimpleLabeledControl({ + scope: '#/properties/authorizationEndpoint', + label: 'Authorization Endpoint:', + description: 'Override auto-discovery (optional)', + controlOptions: { + inputType: 'url', + }, + rule: { + effect: RuleEffect.HIDE, + condition: { + scope: '#/properties/id', + schema: { const: 'unraid.net' }, + }, + }, + }), + createSimpleLabeledControl({ + scope: '#/properties/tokenEndpoint', + label: 'Token Endpoint:', + description: 'Override auto-discovery (optional)', + controlOptions: { + inputType: 'url', + }, + rule: { + effect: RuleEffect.HIDE, + condition: { + scope: '#/properties/id', + schema: { const: 'unraid.net' }, + }, + }, + }), + createSimpleLabeledControl({ + scope: '#/properties/jwksUri', + label: 'JWKS URI:', + description: 'Override auto-discovery (optional)', + controlOptions: { + inputType: 'url', + }, + rule: { + effect: RuleEffect.HIDE, + condition: { + scope: '#/properties/id', + schema: { const: 'unraid.net' }, + }, + }, + }), + ], + }, + { + type: 'VerticalLayout', + options: { + accordion: { + title: 'Authorization Rules', + description: 'Configure who can access your server', + }, + }, + elements: [ + // Authorization Mode Toggle + createSimpleLabeledControl({ + scope: '#/properties/authorizationMode', + label: 'Authorization Mode:', + description: + 'Choose between simple presets or advanced rule configuration', + controlOptions: {}, + }), + // Simple Authorization Fields (shown when mode is 'simple') + { + type: 'VerticalLayout', + rule: { + effect: RuleEffect.SHOW, + condition: { + scope: '#/properties/authorizationMode', + schema: { const: 'simple' }, + }, + }, + elements: [ + { + type: 'Label', + text: 'Simple Authorization', + options: { + description: + 'Configure who can login using simple presets. At least one field must be configured.', + format: 'title', + }, + }, + createSimpleLabeledControl({ + scope: '#/properties/simpleAuthorization/properties/allowedDomains', + label: 'Allowed Email Domains:', + description: + 'Users with emails ending in these domains can login (e.g., company.com)', + controlOptions: { + format: 'array', + inputType: 'text', + placeholder: 'company.com', + }, + }), + createSimpleLabeledControl({ + scope: '#/properties/simpleAuthorization/properties/allowedEmails', + label: 'Specific Email Addresses:', + description: + 'Only these exact email addresses can login', + controlOptions: { + format: 'array', + inputType: 'email', + placeholder: 'user@example.com', + }, + }), + createSimpleLabeledControl({ + scope: '#/properties/simpleAuthorization/properties/allowedUserIds', + label: 'Allowed User IDs:', + description: + 'Specific user IDs from the identity provider', + controlOptions: { + format: 'array', + inputType: 'text', + placeholder: 'user-id-123', + }, + }), + // Google-specific field (shown only for Google providers) + { + type: 'VerticalLayout', + rule: { + effect: RuleEffect.SHOW, + condition: { + scope: '#/properties/issuer', + schema: { pattern: '.*google.*' }, + }, + }, + elements: [ + createSimpleLabeledControl({ + scope: '#/properties/simpleAuthorization/properties/googleWorkspaceDomain', + label: 'Google Workspace Domain:', + description: + 'Restrict to users from your Google Workspace domain', + controlOptions: { + inputType: 'text', + placeholder: 'company.com', + }, + }), + ], + }, + ], + }, + // Advanced Authorization Rules (shown when mode is 'advanced' or authorizationRuleMode is 'and') + { + type: 'VerticalLayout', + rule: { + effect: RuleEffect.SHOW, + condition: { + type: 'OR', + conditions: [ + { + scope: '#/properties/authorizationMode', + schema: { const: 'advanced' }, + }, + { + scope: '#/properties/authorizationRuleMode', + schema: { const: 'AND' }, + }, + ], + }, + }, + elements: [ + { + type: 'Label', + text: 'Advanced Authorization Rules', + options: { + description: + 'Define authorization rules based on claims in the ID token. Rule mode can be configured: OR logic (any rule matches) or AND logic (all rules must match).', + }, + }, + createSimpleLabeledControl({ + scope: '#/properties/authorizationRuleMode', + label: 'Rule Mode:', + description: + 'How to evaluate multiple rules: OR (any rule passes) or AND (all rules must pass)', + controlOptions: {}, + }), + { + type: 'Control', + scope: '#/properties/authorizationRules', + options: { + elementLabelFormat: + '${claim} ${operator}', + itemTypeName: 'Rule', + detail: { + type: 'VerticalLayout', + elements: [ + createSimpleLabeledControl({ + scope: '#/properties/claim', + label: 'JWT Claim:', + description: + 'JWT claim to check (e.g., email, sub, groups, hd for Google hosted domain)', + controlOptions: { + inputType: 'text', + placeholder: 'email', + }, + }), + createSimpleLabeledControl({ + scope: '#/properties/operator', + label: 'Operator:', + description: + 'How to compare the claim value', + controlOptions: {}, + }), + createSimpleLabeledControl({ + scope: '#/properties/value', + label: 'Values:', + description: + 'Value(s) to match against (any match passes)', + controlOptions: { + format: 'array', + inputType: 'text', + placeholder: + '@company.com', + }, + }), + ], + }, + }, + }, + ], + }, + ], + }, + { + type: 'VerticalLayout', + options: { + accordion: { + title: 'Button Customization', + description: + 'Customize the appearance of the login button', + }, + }, + rule: { + effect: RuleEffect.HIDE, + condition: { + scope: '#/properties/id', + schema: { const: 'unraid.net' }, + }, + }, + elements: [ + createSimpleLabeledControl({ + scope: '#/properties/buttonText', + label: 'Button Text:', + description: 'Custom login button text (optional)', + controlOptions: { + inputType: 'text', + placeholder: 'Sign in with Provider', + }, + }), + createSimpleLabeledControl({ + scope: '#/properties/buttonIcon', + label: 'Button Icon URL:', + description: + 'Icon URL or base64 data URI (optional)', + controlOptions: { + inputType: 'url', + }, + }), + createSimpleLabeledControl({ + scope: '#/properties/buttonVariant', + label: 'Button Style:', + description: 'Visual style of the login button', + controlOptions: {}, + }), + createSimpleLabeledControl({ + scope: '#/properties/buttonStyle', + label: 'Custom CSS Styles:', + description: + 'Inline CSS styles for custom button appearance. Buttons with background-color will automatically get hover effects (darkening on hover/active). Examples:\n\nSolid color: "background-color: #ff6600; border-color: #ff6600; color: white;"\nGradient: "background: linear-gradient(45deg, #667eea, #764ba2); border: none; color: white;"\nRounded: "border-radius: 25px; background-color: #10b981; color: white;"', + controlOptions: { + inputType: 'textarea', + placeholder: + 'background-color: #3b82f6; border-color: #3b82f6; color: white; transition: all 0.2s;', + }, + }), + ], + }, + ], + }), + }, + }, + ], + }, + ], + }; + } +} diff --git a/api/src/unraid-api/graph/resolvers/sso/oidc-provider.model.ts b/api/src/unraid-api/graph/resolvers/sso/oidc-provider.model.ts new file mode 100644 index 000000000..163b4065e --- /dev/null +++ b/api/src/unraid-api/graph/resolvers/sso/oidc-provider.model.ts @@ -0,0 +1,168 @@ +import { Field, ObjectType, registerEnumType } from '@nestjs/graphql'; + +import { PrefixedID } from '@unraid/shared/prefixed-id-scalar.js'; +import { Type } from 'class-transformer'; +import { + IsArray, + IsEnum, + IsNotEmpty, + IsOptional, + IsString, + IsUrl, + ValidateNested, +} from 'class-validator'; + +export enum AuthorizationOperator { + EQUALS = 'equals', + CONTAINS = 'contains', + ENDS_WITH = 'endsWith', + STARTS_WITH = 'startsWith', +} + +export enum AuthorizationRuleMode { + OR = 'or', + AND = 'and', +} + +registerEnumType(AuthorizationOperator, { + name: 'AuthorizationOperator', + description: 'Operators for authorization rule matching', +}); + +registerEnumType(AuthorizationRuleMode, { + name: 'AuthorizationRuleMode', + description: + 'Mode for evaluating authorization rules - OR (any rule passes) or AND (all rules must pass)', +}); + +@ObjectType() +export class OidcAuthorizationRule { + @Field(() => String, { description: 'The claim to check (e.g., email, sub, groups, hd)' }) + @IsString() + @IsNotEmpty() + claim!: string; + + @Field(() => AuthorizationOperator, { description: 'The comparison operator' }) + @IsEnum(AuthorizationOperator) + operator!: AuthorizationOperator; + + @Field(() => [String], { description: 'The value(s) to match against' }) + @IsArray() + @IsString({ each: true }) + value!: string[]; +} + +@ObjectType() +export class OidcProvider { + @Field(() => PrefixedID, { description: 'The unique identifier for the OIDC provider' }) + @IsString() + @IsNotEmpty() + id!: string; + + @Field(() => String, { description: 'Display name of the OIDC provider' }) + @IsString() + @IsNotEmpty() + name!: string; + + @Field(() => String, { description: 'OAuth2 client ID registered with the provider' }) + @IsString() + @IsNotEmpty() + clientId!: string; + + @Field(() => String, { + nullable: true, + description: 'OAuth2 client secret (if required by provider)', + }) + @IsString() + @IsOptional() + clientSecret?: string; + + @Field(() => String, { + description: + 'OIDC issuer URL (e.g., https://accounts.google.com). Required for auto-discovery via /.well-known/openid-configuration', + }) + @IsUrl() + issuer!: string; + + @Field(() => String, { + nullable: true, + description: + 'OAuth2 authorization endpoint URL. If omitted, will be auto-discovered from issuer/.well-known/openid-configuration', + }) + @IsUrl() + @IsOptional() + authorizationEndpoint?: string; + + @Field(() => String, { + nullable: true, + description: + 'OAuth2 token endpoint URL. If omitted, will be auto-discovered from issuer/.well-known/openid-configuration', + }) + @IsUrl() + @IsOptional() + tokenEndpoint?: string; + + @Field(() => String, { + nullable: true, + description: + 'JSON Web Key Set URI for token validation. If omitted, will be auto-discovered from issuer/.well-known/openid-configuration', + }) + @IsUrl() + @IsOptional() + jwksUri?: string; + + @Field(() => [String], { description: 'OAuth2 scopes to request (e.g., openid, profile, email)' }) + @IsArray() + @IsString({ each: true }) + scopes!: string[]; + + @Field(() => [OidcAuthorizationRule], { + nullable: true, + description: 'Flexible authorization rules based on claims', + }) + @IsArray() + @ValidateNested({ each: true }) + @Type(() => OidcAuthorizationRule) + @IsOptional() + authorizationRules?: OidcAuthorizationRule[]; + + @Field(() => AuthorizationRuleMode, { + nullable: true, + description: + 'Mode for evaluating authorization rules - OR (any rule passes) or AND (all rules must pass). Defaults to OR.', + defaultValue: AuthorizationRuleMode.OR, + }) + @IsEnum(AuthorizationRuleMode) + @IsOptional() + authorizationRuleMode?: AuthorizationRuleMode; + + @Field(() => String, { nullable: true, description: 'Custom text for the login button' }) + @IsString() + @IsOptional() + buttonText?: string; + + @Field(() => String, { + nullable: true, + description: 'URL or base64 encoded icon for the login button', + }) + @IsString() + @IsOptional() + buttonIcon?: string; + + @Field(() => String, { + nullable: true, + description: 'Button variant style from Reka UI. See https://reka-ui.com/docs/components/button', + }) + @IsString() + @IsOptional() + buttonVariant?: string; + + @Field(() => String, { + nullable: true, + description: + 'Custom CSS styles for the button (e.g., "background: linear-gradient(to right, #4f46e5, #7c3aed); border-radius: 9999px;")', + }) + @IsString() + @IsOptional() + buttonStyle?: string; +} diff --git a/api/src/unraid-api/graph/resolvers/sso/oidc-session-validation.model.ts b/api/src/unraid-api/graph/resolvers/sso/oidc-session-validation.model.ts new file mode 100644 index 000000000..04af9c776 --- /dev/null +++ b/api/src/unraid-api/graph/resolvers/sso/oidc-session-validation.model.ts @@ -0,0 +1,10 @@ +import { Field, ObjectType } from '@nestjs/graphql'; + +@ObjectType() +export class OidcSessionValidation { + @Field(() => Boolean) + valid!: boolean; + + @Field(() => String, { nullable: true }) + username?: string; +} diff --git a/api/src/unraid-api/graph/resolvers/sso/oidc-session.service.spec.ts b/api/src/unraid-api/graph/resolvers/sso/oidc-session.service.spec.ts new file mode 100644 index 000000000..3fe1a448e --- /dev/null +++ b/api/src/unraid-api/graph/resolvers/sso/oidc-session.service.spec.ts @@ -0,0 +1,121 @@ +import { CACHE_MANAGER } from '@nestjs/cache-manager'; +import { Test } from '@nestjs/testing'; + +import type { Cache } from 'cache-manager'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import { OidcSessionService } from '@app/unraid-api/graph/resolvers/sso/oidc-session.service.js'; + +describe('OidcSessionService', () => { + let service: OidcSessionService; + let cacheManager: Cache; + + beforeEach(async () => { + const mockCacheManager = { + get: vi.fn(), + set: vi.fn(), + del: vi.fn(), + }; + + const module = await Test.createTestingModule({ + providers: [ + OidcSessionService, + { + provide: CACHE_MANAGER, + useValue: mockCacheManager, + }, + ], + }).compile(); + + service = module.get(OidcSessionService); + cacheManager = module.get(CACHE_MANAGER); + }); + + describe('one-time token validation', () => { + it('should validate a token successfully on first attempt', async () => { + // Create a session + const token = await service.createSession('test-provider', 'test-user-id'); + + // Mock cache get to return the session + vi.mocked(cacheManager.get).mockResolvedValueOnce({ + id: expect.any(String), + providerId: 'test-provider', + providerUserId: 'test-user-id', + createdAt: new Date(), + expiresAt: new Date(Date.now() + 5 * 60 * 1000), + }); + + // First validation should succeed + const result = await service.validateSession(token); + expect(result.valid).toBe(true); + expect(result.username).toBe('root'); + expect(cacheManager.del).toHaveBeenCalled(); + }); + + it('should fail validation on second attempt with same token', async () => { + // Create a session + const token = await service.createSession('test-provider', 'test-user-id'); + + // Mock cache get for first validation + vi.mocked(cacheManager.get).mockResolvedValueOnce({ + id: expect.any(String), + providerId: 'test-provider', + providerUserId: 'test-user-id', + createdAt: new Date(), + expiresAt: new Date(Date.now() + 5 * 60 * 1000), + }); + + // First validation should succeed + const firstResult = await service.validateSession(token); + expect(firstResult.valid).toBe(true); + + // Mock cache get for second validation (session deleted) + vi.mocked(cacheManager.get).mockResolvedValueOnce(null); + + // Second validation should fail (token already used) + const secondResult = await service.validateSession(token); + expect(secondResult.valid).toBe(false); + expect(secondResult.username).toBeUndefined(); + }); + + it('should handle invalid token format', async () => { + const result = await service.validateSession('invalid-token'); + expect(result.valid).toBe(false); + expect(result.username).toBeUndefined(); + expect(cacheManager.get).not.toHaveBeenCalled(); + }); + + it('should handle non-existent session ID', async () => { + // Create a fake token with valid format but non-existent session ID + const fakeToken = + 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Im9pZGMtc2Vzc2lvbiJ9.eyJzdWIiOiJvaWRjLXNlc3Npb24iLCJpc3MiOiJ1bnJhaWQtYXBpIiwiYXVkIjoibG9jYWxob3N0IiwiaWF0IjoxNzAwMDAwMDAwLCJleHAiOjk5OTk5OTk5OTksIm5vbmNlIjoicGFkZGluZy1mb3ItbGVuZ3RoIn0.OIDC-SESSION-00000000-0000-0000-0000-000000000000-xxxxxxxx'; + + // Mock cache get to return null (session not found) + vi.mocked(cacheManager.get).mockResolvedValueOnce(null); + + const result = await service.validateSession(fakeToken); + expect(result.valid).toBe(false); + expect(result.username).toBeUndefined(); + }); + + it('should handle expired sessions', async () => { + // Create a session + const token = await service.createSession('test-provider', 'test-user-id'); + + // Mock cache get to return an expired session + vi.mocked(cacheManager.get).mockResolvedValueOnce({ + id: expect.any(String), + providerId: 'test-provider', + providerUserId: 'test-user-id', + createdAt: new Date(Date.now() - 10 * 60 * 1000), + expiresAt: new Date(Date.now() - 5 * 60 * 1000), // Expired 5 minutes ago + }); + + // Validation should fail due to expiration + const result = await service.validateSession(token); + expect(result.valid).toBe(false); + expect(result.username).toBeUndefined(); + expect(cacheManager.del).toHaveBeenCalled(); + }); + }); +}); diff --git a/api/src/unraid-api/graph/resolvers/sso/oidc-session.service.ts b/api/src/unraid-api/graph/resolvers/sso/oidc-session.service.ts new file mode 100644 index 000000000..9f9e87b53 --- /dev/null +++ b/api/src/unraid-api/graph/resolvers/sso/oidc-session.service.ts @@ -0,0 +1,101 @@ +import { CACHE_MANAGER } from '@nestjs/cache-manager'; +import { Inject, Injectable, Logger } from '@nestjs/common'; +import { randomUUID } from 'node:crypto'; + +import type { Cache } from 'cache-manager'; + +export interface OidcSession { + id: string; + providerId: string; + providerUserId: string; + createdAt: Date; + expiresAt: Date; +} + +@Injectable() +export class OidcSessionService { + private readonly logger = new Logger(OidcSessionService.name); + private readonly SESSION_TTL_SECONDS = 2 * 60; // 2 minutes for one-time token security + + constructor(@Inject(CACHE_MANAGER) private readonly cacheManager: Cache) {} + + async createSession(providerId: string, providerUserId: string): Promise { + const sessionId = randomUUID(); + const now = new Date(); + + const session: OidcSession = { + id: sessionId, + providerId, + providerUserId, + createdAt: now, + expiresAt: new Date(now.getTime() + this.SESSION_TTL_SECONDS * 1000), + }; + + // Store in cache with TTL + await this.cacheManager.set(sessionId, session, this.SESSION_TTL_SECONDS * 1000); + this.logger.log(`Created OIDC session ${sessionId} for provider ${providerId}`); + + return this.createPaddedToken(sessionId); + } + + async validateSession(token: string): Promise<{ valid: boolean; username?: string }> { + const sessionId = this.extractSessionId(token); + if (!sessionId) { + return { valid: false }; + } + + const session = await this.cacheManager.get(sessionId); + if (!session) { + this.logger.debug(`Session ${sessionId} not found`); + return { valid: false }; + } + + const now = new Date(); + if (now > new Date(session.expiresAt)) { + this.logger.debug(`Session ${sessionId} expired`); + await this.cacheManager.del(sessionId); + return { valid: false }; + } + + // Delete the session immediately after successful validation + // This ensures the token can only be validated once + await this.cacheManager.del(sessionId); + + this.logger.log( + `Validated and invalidated session ${sessionId} for provider ${session.providerId} (one-time use)` + ); + return { valid: true, username: 'root' }; + } + + private createPaddedToken(sessionId: string): string { + // Create a fake JWT structure to exceed 500 characters + // Format: header.payload.signature where signature contains our UUID + const fakeHeader = 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Im9pZGMtc2Vzc2lvbiJ9'; + const fakePayload = + 'eyJzdWIiOiJvaWRjLXNlc3Npb24iLCJpc3MiOiJ1bnJhaWQtYXBpIiwiYXVkIjoibG9jYWxob3N0IiwiaWF0IjoxNzAwMDAwMDAwLCJleHAiOjk5OTk5OTk5OTksIm5vbmNlIjoicGFkZGluZy1mb3ItbGVuZ3RoIn0'; + + // Embed the session ID in the signature part with padding + const signaturePart = `OIDC-SESSION-${sessionId}-` + 'x'.repeat(400); + + return `${fakeHeader}.${fakePayload}.${signaturePart}`; + } + + private extractSessionId(token: string): string | null { + try { + const parts = token.split('.'); + if (parts.length !== 3) { + return null; + } + + const signature = parts[2]; + const match = signature.match(/^OIDC-SESSION-([a-f0-9-]+)-/); + if (!match) { + return null; + } + + return match[1]; + } catch { + return null; + } + } +} diff --git a/api/src/unraid-api/graph/resolvers/sso/oidc-state.service.spec.ts b/api/src/unraid-api/graph/resolvers/sso/oidc-state.service.spec.ts new file mode 100644 index 000000000..5052864a9 --- /dev/null +++ b/api/src/unraid-api/graph/resolvers/sso/oidc-state.service.spec.ts @@ -0,0 +1,204 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +import { OidcStateService } from '@app/unraid-api/graph/resolvers/sso/oidc-state.service.js'; + +describe('OidcStateService', () => { + let service: OidcStateService; + + beforeEach(() => { + vi.clearAllMocks(); + vi.useFakeTimers(); + // Create a single instance for all tests in a describe block + service = new OidcStateService(); + }); + + afterEach(() => { + vi.useRealTimers(); + }); + + describe('generateSecureState', () => { + it('should generate a state with provider prefix and signed token', () => { + const providerId = 'test-provider'; + const clientState = 'client-state-123'; + + const state = service.generateSecureState(providerId, clientState); + + expect(state).toBeTruthy(); + expect(typeof state).toBe('string'); + expect(state.startsWith(`${providerId}:`)).toBe(true); + + // Extract signed portion and verify format (nonce.timestamp.signature) + const signed = state.substring(providerId.length + 1); + expect(signed.split('.').length).toBe(3); + }); + + it('should generate unique states for each call', () => { + const providerId = 'test-provider'; + const clientState = 'client-state-123'; + + const state1 = service.generateSecureState(providerId, clientState); + const state2 = service.generateSecureState(providerId, clientState); + + expect(state1).not.toBe(state2); + }); + }); + + describe('validateSecureState', () => { + it('should validate a valid state token', () => { + const providerId = 'test-provider'; + const clientState = 'client-state-123'; + + const state = service.generateSecureState(providerId, clientState); + const result = service.validateSecureState(state, providerId); + + expect(result.isValid).toBe(true); + expect(result.clientState).toBe(clientState); + expect(result.error).toBeUndefined(); + }); + + it('should reject state with wrong provider ID', () => { + const providerId = 'test-provider'; + const clientState = 'client-state-123'; + + const state = service.generateSecureState(providerId, clientState); + const result = service.validateSecureState(state, 'wrong-provider'); + + expect(result.isValid).toBe(false); + expect(result.error).toBe('Provider ID mismatch in state'); + }); + + it('should reject expired state tokens', () => { + const providerId = 'test-provider'; + const clientState = 'client-state-123'; + + const state = service.generateSecureState(providerId, clientState); + + // Fast forward time beyond expiration (11 minutes) + vi.advanceTimersByTime(11 * 60 * 1000); + + const result = service.validateSecureState(state, providerId); + + expect(result.isValid).toBe(false); + expect(result.error).toBe('State token has expired'); + }); + + it('should reject reused state tokens', () => { + const providerId = 'test-provider'; + const clientState = 'client-state-123'; + + const state = service.generateSecureState(providerId, clientState); + + // First validation should succeed + const result1 = service.validateSecureState(state, providerId); + expect(result1.isValid).toBe(true); + + // Second validation should fail (replay attack prevention) + const result2 = service.validateSecureState(state, providerId); + expect(result2.isValid).toBe(false); + expect(result2.error).toBe('State token not found or already used'); + }); + + it('should reject invalid state tokens', () => { + const result = service.validateSecureState('invalid.state.token', 'test-provider'); + + expect(result.isValid).toBe(false); + expect(result.error).toBe('Invalid state format'); + }); + + it('should reject tampered state tokens', () => { + const providerId = 'test-provider'; + const clientState = 'client-state-123'; + + const state = service.generateSecureState(providerId, clientState); + + // Tamper with the signature + const parts = state.split('.'); + parts[2] = parts[2].slice(0, -4) + 'XXXX'; + const tamperedState = parts.join('.'); + + const result = service.validateSecureState(tamperedState, providerId); + + expect(result.isValid).toBe(false); + expect(result.error).toBe('Invalid state signature'); + }); + }); + + describe('extractProviderFromState', () => { + it('should extract provider from state prefix', () => { + const state = 'provider-id:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.payload.signature'; + const result = service.extractProviderFromState(state); + + expect(result).toBe('provider-id'); + }); + + it('should handle states with multiple colons', () => { + const state = 'provider-id:jwt:with:colons'; + const result = service.extractProviderFromState(state); + + expect(result).toBe('provider-id'); + }); + + it('should return null for invalid format', () => { + const result = service.extractProviderFromState('invalid-state'); + + expect(result).toBeNull(); + }); + }); + + describe('extractProviderFromLegacyState', () => { + it('should extract provider from legacy colon-separated format', () => { + const result = service.extractProviderFromLegacyState('provider-id:client-state'); + + expect(result.providerId).toBe('provider-id'); + expect(result.originalState).toBe('client-state'); + }); + + it('should handle multiple colons in legacy format', () => { + const result = service.extractProviderFromLegacyState( + 'provider-id:client:state:with:colons' + ); + + expect(result.providerId).toBe('provider-id'); + expect(result.originalState).toBe('client:state:with:colons'); + }); + + it('should return empty provider for JWT format', () => { + const jwtState = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.payload.signature'; + const result = service.extractProviderFromLegacyState(jwtState); + + expect(result.providerId).toBe(''); + expect(result.originalState).toBe(jwtState); + }); + + it('should return empty provider for unknown format', () => { + const result = service.extractProviderFromLegacyState('some-random-state'); + + expect(result.providerId).toBe(''); + expect(result.originalState).toBe('some-random-state'); + }); + }); + + describe('cleanupExpiredStates', () => { + it('should clean up expired states periodically', () => { + const providerId = 'test-provider'; + + // Generate multiple states + service.generateSecureState(providerId, 'state1'); + service.generateSecureState(providerId, 'state2'); + service.generateSecureState(providerId, 'state3'); + + // Fast forward past expiration + vi.advanceTimersByTime(11 * 60 * 1000); + + // Generate a new state that shouldn't be cleaned + const validState = service.generateSecureState(providerId, 'state4'); + + // Trigger cleanup (happens every minute) + vi.advanceTimersByTime(60 * 1000); + + // The new state should still be valid + const result = service.validateSecureState(validState, providerId); + expect(result.isValid).toBe(true); + }); + }); +}); diff --git a/api/src/unraid-api/graph/resolvers/sso/oidc-state.service.ts b/api/src/unraid-api/graph/resolvers/sso/oidc-state.service.ts new file mode 100644 index 000000000..50a17a15b --- /dev/null +++ b/api/src/unraid-api/graph/resolvers/sso/oidc-state.service.ts @@ -0,0 +1,201 @@ +import { Injectable, Logger } from '@nestjs/common'; +import crypto from 'crypto'; + +interface StateData { + nonce: string; + clientState: string; + timestamp: number; + providerId: string; +} + +@Injectable() +export class OidcStateService { + private readonly logger = new Logger(OidcStateService.name); + private readonly stateCache = new Map(); + private readonly hmacSecret: string; + private readonly STATE_TTL_SECONDS = 600; // 10 minutes + + constructor() { + // Always generate a new secret on API restart for security + // This ensures state tokens cannot be reused across restarts + this.hmacSecret = crypto.randomBytes(32).toString('hex'); + this.logger.debug('Generated new OIDC state secret for this session'); + + // Clean up expired states periodically + setInterval(() => this.cleanupExpiredStates(), 60000); // Every minute + } + + generateSecureState(providerId: string, clientState: string): string { + const nonce = crypto.randomBytes(16).toString('hex'); + const timestamp = Date.now(); + + // Store state data in cache + const stateData: StateData = { + nonce, + clientState, + timestamp, + providerId, + }; + this.stateCache.set(nonce, stateData); + + // Create signed state: nonce.timestamp.signature + const dataToSign = `${nonce}.${timestamp}`; + const signature = crypto.createHmac('sha256', this.hmacSecret).update(dataToSign).digest('hex'); + + const signedState = `${dataToSign}.${signature}`; + + this.logger.debug(`Generated secure state for provider ${providerId} with nonce ${nonce}`); + // Return state with provider ID prefix (unencrypted) for routing + return `${providerId}:${signedState}`; + } + + validateSecureState( + state: string, + expectedProviderId: string + ): { isValid: boolean; clientState?: string; error?: string } { + try { + // Extract provider ID and signed state + const parts = state.split(':'); + if (parts.length < 2) { + return { + isValid: false, + error: 'Invalid state format', + }; + } + + const providerId = parts[0]; + const signedState = parts.slice(1).join(':'); + + // Validate provider ID matches + if (providerId !== expectedProviderId) { + this.logger.warn( + `State validation failed: provider mismatch. Expected ${expectedProviderId}, got ${providerId}` + ); + return { + isValid: false, + error: 'Provider ID mismatch in state', + }; + } + + // Parse and verify signature + const stateParts = signedState.split('.'); + if (stateParts.length !== 3) { + return { + isValid: false, + error: 'Invalid state format', + }; + } + + const [nonce, timestampStr, signature] = stateParts; + const timestamp = parseInt(timestampStr, 10); + + // Verify signature + const dataToSign = `${nonce}.${timestampStr}`; + const expectedSignature = crypto + .createHmac('sha256', this.hmacSecret) + .update(dataToSign) + .digest('hex'); + + if (signature !== expectedSignature) { + this.logger.warn(`State validation failed: invalid signature`); + return { + isValid: false, + error: 'Invalid state signature', + }; + } + + // Check timestamp expiration + const now = Date.now(); + const age = now - timestamp; + if (age > this.STATE_TTL_SECONDS * 1000) { + this.logger.warn(`State validation failed: token expired (age: ${age}ms)`); + return { + isValid: false, + error: 'State token has expired', + }; + } + + // Check if state exists in cache (prevents replay attacks) + const cachedState = this.stateCache.get(nonce); + if (!cachedState) { + this.logger.warn( + `State validation failed: nonce ${nonce} not found in cache (possible replay attack)` + ); + return { + isValid: false, + error: 'State token not found or already used', + }; + } + + // Verify the cached provider ID matches + if (cachedState.providerId !== expectedProviderId) { + this.logger.warn(`State validation failed: cached provider mismatch`); + return { + isValid: false, + error: 'Invalid state token', + }; + } + + // Remove from cache to prevent reuse + this.stateCache.delete(nonce); + + this.logger.debug(`State validation successful for provider ${expectedProviderId}`); + return { + isValid: true, + clientState: cachedState.clientState, + }; + } catch (error) { + this.logger.error( + `State validation error: ${error instanceof Error ? error.message : 'Unknown error'}` + ); + return { + isValid: false, + error: 'Invalid state token', + }; + } + } + + extractProviderFromLegacyState(state: string): { providerId: string; originalState: string } { + // Backward compatibility: handle old format states + const parts = state.split(':'); + if (parts.length >= 2 && !state.includes('.')) { + // Old format: providerId:clientState + return { + providerId: parts[0], + originalState: parts.slice(1).join(':'), + }; + } + + // New format (JWT) or unknown format + return { + providerId: '', + originalState: state, + }; + } + + extractProviderFromState(state: string): string | null { + // Extract provider ID from state prefix (no decryption needed) + const parts = state.split(':'); + if (parts.length >= 2) { + return parts[0]; + } + return null; + } + + private cleanupExpiredStates(): void { + const now = Date.now(); + let cleaned = 0; + + for (const [nonce, stateData] of this.stateCache.entries()) { + const age = now - stateData.timestamp; + if (age > this.STATE_TTL_SECONDS * 1000) { + this.stateCache.delete(nonce); + cleaned++; + } + } + + if (cleaned > 0) { + this.logger.debug(`Cleaned up ${cleaned} expired state entries`); + } + } +} diff --git a/api/src/unraid-api/graph/resolvers/sso/oidc-validation.service.ts b/api/src/unraid-api/graph/resolvers/sso/oidc-validation.service.ts new file mode 100644 index 000000000..bbdc814c6 --- /dev/null +++ b/api/src/unraid-api/graph/resolvers/sso/oidc-validation.service.ts @@ -0,0 +1,164 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; + +import * as client from 'openid-client'; + +import { OidcProvider } from '@app/unraid-api/graph/resolvers/sso/oidc-provider.model.js'; + +@Injectable() +export class OidcValidationService { + private readonly logger = new Logger(OidcValidationService.name); + + constructor(private readonly configService: ConfigService) {} + + /** + * Validate OIDC provider configuration by attempting discovery + * Returns validation result with helpful error messages for debugging + */ + async validateProvider( + provider: OidcProvider + ): Promise<{ isValid: boolean; error?: string; details?: unknown }> { + try { + // Validate issuer URL is present + if (!provider.issuer) { + return { + isValid: false, + error: 'No issuer URL provided. Please specify the OIDC provider issuer URL.', + details: { type: 'MISSING_ISSUER' }, + }; + } + + // Validate issuer URL is valid + let serverUrl: URL; + try { + serverUrl = new URL(provider.issuer); + } catch (urlError) { + return { + isValid: false, + error: `Invalid issuer URL format: '${provider.issuer}'. Please provide a valid URL.`, + details: { + type: 'INVALID_URL', + originalError: urlError instanceof Error ? urlError.message : String(urlError), + }, + }; + } + + // Configure client options for HTTP if needed + let clientOptions: any = undefined; + if (serverUrl.protocol === 'http:') { + this.logger.debug( + `HTTP issuer URL detected for provider ${provider.id}: ${provider.issuer}` + ); + clientOptions = { + execute: [client.allowInsecureRequests], + }; + } + + // Attempt OIDC discovery + await this.performDiscovery(provider, clientOptions); + return { isValid: true }; + } catch (error) { + const errorMessage = error instanceof Error ? error.message : 'Unknown error'; + + // Log the raw error for debugging + this.logger.debug(`Raw discovery error for ${provider.id}: ${errorMessage}`); + + // Provide specific error messages for common issues + let userFriendlyError = errorMessage; + let details: Record = {}; + + if (errorMessage.includes('getaddrinfo ENOTFOUND')) { + userFriendlyError = `Cannot resolve domain name. Please check that '${provider.issuer}' is accessible and spelled correctly.`; + details = { type: 'DNS_ERROR', originalError: errorMessage }; + } else if (errorMessage.includes('ECONNREFUSED')) { + userFriendlyError = `Connection refused. The server at '${provider.issuer}' is not accepting connections.`; + details = { type: 'CONNECTION_ERROR', originalError: errorMessage }; + } else if (errorMessage.includes('ECONNRESET') || errorMessage.includes('ETIMEDOUT')) { + userFriendlyError = `Connection timeout. The server at '${provider.issuer}' is not responding.`; + details = { type: 'TIMEOUT_ERROR', originalError: errorMessage }; + } else if (errorMessage.includes('404') || errorMessage.includes('Not Found')) { + const baseUrl = provider.issuer?.endsWith('/.well-known/openid-configuration') + ? provider.issuer.replace('/.well-known/openid-configuration', '') + : provider.issuer; + userFriendlyError = `OIDC discovery endpoint not found. Please verify that '${baseUrl}/.well-known/openid-configuration' exists.`; + details = { type: 'DISCOVERY_NOT_FOUND', originalError: errorMessage }; + } else if (errorMessage.includes('401') || errorMessage.includes('403')) { + userFriendlyError = `Access denied to discovery endpoint. Please check the issuer URL and any authentication requirements.`; + details = { type: 'AUTHENTICATION_ERROR', originalError: errorMessage }; + } else if (errorMessage.includes('unexpected HTTP response status code')) { + // Extract status code if possible + const statusMatch = errorMessage.match(/status code (\d+)/); + const statusCode = statusMatch ? statusMatch[1] : 'unknown'; + const baseUrl = provider.issuer?.endsWith('/.well-known/openid-configuration') + ? provider.issuer.replace('/.well-known/openid-configuration', '') + : provider.issuer; + userFriendlyError = `HTTP ${statusCode} error from discovery endpoint. Please check that '${baseUrl}/.well-known/openid-configuration' returns a valid OIDC discovery document.`; + details = { type: 'HTTP_STATUS_ERROR', statusCode, originalError: errorMessage }; + } else if ( + errorMessage.includes('certificate') || + errorMessage.includes('SSL') || + errorMessage.includes('TLS') + ) { + userFriendlyError = `SSL/TLS certificate error. The server certificate may be invalid or expired.`; + details = { type: 'SSL_ERROR', originalError: errorMessage }; + } else if (errorMessage.includes('JSON') || errorMessage.includes('parse')) { + userFriendlyError = `Invalid OIDC discovery response. The server returned malformed JSON.`; + details = { type: 'INVALID_JSON', originalError: errorMessage }; + } else if (error && (error as any).code === 'OAUTH_RESPONSE_IS_NOT_CONFORM') { + const baseUrl = provider.issuer?.endsWith('/.well-known/openid-configuration') + ? provider.issuer.replace('/.well-known/openid-configuration', '') + : provider.issuer; + userFriendlyError = `Invalid OIDC discovery document. The server at '${baseUrl}/.well-known/openid-configuration' returned a response that doesn't conform to the OpenID Connect Discovery specification. Please verify the endpoint returns valid OIDC metadata.`; + details = { type: 'INVALID_OIDC_DOCUMENT', originalError: errorMessage }; + } + + this.logger.warn(`OIDC validation failed for provider ${provider.id}: ${errorMessage}`); + + // Add debug logging for HTTP status errors + if (errorMessage.includes('unexpected HTTP response status code')) { + const baseUrl = provider.issuer?.endsWith('/.well-known/openid-configuration') + ? provider.issuer.replace('/.well-known/openid-configuration', '') + : provider.issuer; + this.logger.debug(`Attempted to fetch: ${baseUrl}/.well-known/openid-configuration`); + this.logger.debug(`Full error details: ${errorMessage}`); + } + + return { + isValid: false, + error: userFriendlyError, + details, + }; + } + } + + async performDiscovery(provider: OidcProvider, clientOptions?: any): Promise { + if (!provider.issuer) { + throw new Error('No issuer URL provided'); + } + + // Configure client auth method + const clientAuth = provider.clientSecret + ? client.ClientSecretPost(provider.clientSecret) + : undefined; + + const serverUrl = new URL(provider.issuer); + + // Use provided client options or create default options with HTTP support if needed + if (!clientOptions && serverUrl.protocol === 'http:') { + this.logger.debug(`Allowing HTTP for ${provider.id} as specified by user`); + // For openid-client v6, use allowInsecureRequests in the execute array + // This is deprecated but needed for local development with HTTP endpoints + clientOptions = { + execute: [client.allowInsecureRequests], + }; + } + + return client.discovery( + serverUrl, + provider.clientId, + undefined, // client metadata + clientAuth, + clientOptions + ); + } +} diff --git a/api/src/unraid-api/graph/resolvers/sso/public-oidc-provider.model.ts b/api/src/unraid-api/graph/resolvers/sso/public-oidc-provider.model.ts new file mode 100644 index 000000000..e34639518 --- /dev/null +++ b/api/src/unraid-api/graph/resolvers/sso/public-oidc-provider.model.ts @@ -0,0 +1,22 @@ +import { Field, ID, ObjectType } from '@nestjs/graphql'; + +@ObjectType() +export class PublicOidcProvider { + @Field(() => ID) + id!: string; + + @Field(() => String, { nullable: false }) + name!: string; + + @Field(() => String, { nullable: true }) + buttonText?: string; + + @Field(() => String, { nullable: true }) + buttonIcon?: string; + + @Field(() => String, { nullable: true }) + buttonVariant?: string; + + @Field(() => String, { nullable: true }) + buttonStyle?: string; +} diff --git a/api/src/unraid-api/graph/resolvers/sso/sso-settings.types.ts b/api/src/unraid-api/graph/resolvers/sso/sso-settings.types.ts new file mode 100644 index 000000000..fafa5bada --- /dev/null +++ b/api/src/unraid-api/graph/resolvers/sso/sso-settings.types.ts @@ -0,0 +1,7 @@ +import type { OidcConfig } from '@app/unraid-api/graph/resolvers/sso/oidc-config.service.js'; + +declare module '@unraid/shared/services/user-settings.js' { + interface UserSettings { + sso: OidcConfig; + } +} diff --git a/api/src/unraid-api/graph/resolvers/sso/sso.module.ts b/api/src/unraid-api/graph/resolvers/sso/sso.module.ts new file mode 100644 index 000000000..45bb92d70 --- /dev/null +++ b/api/src/unraid-api/graph/resolvers/sso/sso.module.ts @@ -0,0 +1,33 @@ +import { CacheModule } from '@nestjs/cache-manager'; +import { Module } from '@nestjs/common'; + +import { UserSettingsModule } from '@unraid/shared/services/user-settings.js'; + +import { OidcAuthService } from '@app/unraid-api/graph/resolvers/sso/oidc-auth.service.js'; +import { OidcConfigPersistence } from '@app/unraid-api/graph/resolvers/sso/oidc-config.service.js'; +import { OidcSessionService } from '@app/unraid-api/graph/resolvers/sso/oidc-session.service.js'; +import { OidcStateService } from '@app/unraid-api/graph/resolvers/sso/oidc-state.service.js'; +import { OidcValidationService } from '@app/unraid-api/graph/resolvers/sso/oidc-validation.service.js'; +import { SsoResolver } from '@app/unraid-api/graph/resolvers/sso/sso.resolver.js'; + +import '@app/unraid-api/graph/resolvers/sso/sso-settings.types.js'; + +@Module({ + imports: [UserSettingsModule, CacheModule.register()], + providers: [ + SsoResolver, + OidcConfigPersistence, + OidcSessionService, + OidcStateService, + OidcAuthService, + OidcValidationService, + ], + exports: [ + OidcConfigPersistence, + OidcSessionService, + OidcStateService, + OidcAuthService, + OidcValidationService, + ], +}) +export class SsoModule {} diff --git a/api/src/unraid-api/graph/resolvers/sso/sso.resolver.ts b/api/src/unraid-api/graph/resolvers/sso/sso.resolver.ts new file mode 100644 index 000000000..4d4da9dd7 --- /dev/null +++ b/api/src/unraid-api/graph/resolvers/sso/sso.resolver.ts @@ -0,0 +1,107 @@ +import { Logger } from '@nestjs/common'; +import { Args, Query, Resolver } from '@nestjs/graphql'; + +import { PrefixedID } from '@unraid/shared/prefixed-id-scalar.js'; +import { + AuthActionVerb, + AuthPossession, + UsePermissions, +} from '@unraid/shared/use-permissions.directive.js'; + +import { Public } from '@app/unraid-api/auth/public.decorator.js'; +import { OidcConfigPersistence } from '@app/unraid-api/graph/resolvers/sso/oidc-config.service.js'; +import { OidcProvider } from '@app/unraid-api/graph/resolvers/sso/oidc-provider.model.js'; +import { OidcSessionValidation } from '@app/unraid-api/graph/resolvers/sso/oidc-session-validation.model.js'; +import { OidcSessionService } from '@app/unraid-api/graph/resolvers/sso/oidc-session.service.js'; +import { PublicOidcProvider } from '@app/unraid-api/graph/resolvers/sso/public-oidc-provider.model.js'; + +@Resolver() +export class SsoResolver { + private readonly logger = new Logger(SsoResolver.name); + + constructor( + private readonly oidcConfig: OidcConfigPersistence, + private readonly oidcSessionService: OidcSessionService + ) {} + + @Query(() => [PublicOidcProvider], { + description: 'Get public OIDC provider information for login buttons', + }) + @Public() + public async publicOidcProviders(): Promise { + const providers = await this.oidcConfig.getProviders(); + + // Filter out providers without valid authorization rules + const providersWithRules = providers.filter((provider) => { + // Check if provider has authorization rules + if (!provider.authorizationRules || provider.authorizationRules.length === 0) { + this.logger.debug( + `Hiding provider ${provider.id} from login page - no authorization rules configured` + ); + return false; + } + + // Check if at least one rule is complete and valid + const hasValidRules = provider.authorizationRules.some( + (rule) => + rule.claim && // Has a claim specified + rule.operator && // Has an operator specified + rule.value && // Has values array + rule.value.length > 0 && // Has at least one value + rule.value.some((v) => v && v.trim() !== '') // At least one non-empty value + ); + + if (!hasValidRules) { + this.logger.debug( + `Hiding provider ${provider.id} from login page - no valid rule values` + ); + return false; + } + + return true; + }); + + return providersWithRules.map((provider) => ({ + id: provider.id, + name: provider.name, + buttonText: provider.buttonText, + buttonIcon: provider.buttonIcon, + buttonVariant: provider.buttonVariant, + buttonStyle: provider.buttonStyle, + })); + } + + @Query(() => [OidcProvider], { description: 'Get all configured OIDC providers (admin only)' }) + @UsePermissions({ + action: AuthActionVerb.READ, + resource: 'sso', + possession: AuthPossession.ANY, + }) + public async oidcProviders(): Promise { + return this.oidcConfig.getProviders(); + } + + @Query(() => OidcProvider, { nullable: true, description: 'Get a specific OIDC provider by ID' }) + @UsePermissions({ + action: AuthActionVerb.READ, + resource: 'sso', + possession: AuthPossession.ANY, + }) + public async oidcProvider( + @Args('id', { type: () => PrefixedID }) id: string + ): Promise { + return this.oidcConfig.getProvider(id); + } + + @Query(() => OidcSessionValidation, { + description: 'Validate an OIDC session token (internal use for CLI validation)', + }) + @UsePermissions({ + action: AuthActionVerb.READ, + resource: 'sso', + possession: AuthPossession.ANY, + }) + public async validateOidcSession(@Args('token') token: string): Promise { + return await this.oidcSessionService.validateSession(token); + } +} diff --git a/api/src/unraid-api/graph/sandbox-plugin.ts b/api/src/unraid-api/graph/sandbox-plugin.ts index 799d0b8d6..f1bdcb00e 100644 --- a/api/src/unraid-api/graph/sandbox-plugin.ts +++ b/api/src/unraid-api/graph/sandbox-plugin.ts @@ -28,34 +28,23 @@ const preconditionFailed = (preconditionName: string) => { throw new HttpException(`Precondition failed: ${preconditionName} `, HttpStatus.PRECONDITION_FAILED); }; -export const getPluginBasedOnSandbox = async (sandbox: boolean, csrfToken: string) => { - if (sandbox) { - const { ApolloServerPluginLandingPageLocalDefault } = await import( - '@apollo/server/plugin/landingPage/default' - ); - const plugin = ApolloServerPluginLandingPageLocalDefault({ - footer: false, - includeCookies: true, - document: initialDocument, - embed: { - initialState: { - sharedHeaders: { - 'x-csrf-token': csrfToken, - }, +export const getSandboxPlugin = async (csrfToken: string) => { + const { ApolloServerPluginLandingPageLocalDefault } = await import( + '@apollo/server/plugin/landingPage/default' + ); + const plugin = ApolloServerPluginLandingPageLocalDefault({ + footer: false, + includeCookies: true, + document: initialDocument, + embed: { + initialState: { + sharedHeaders: { + 'x-csrf-token': csrfToken, }, }, - }); - return plugin; - } else { - const { ApolloServerPluginLandingPageProductionDefault } = await import( - '@apollo/server/plugin/landingPage/default' - ); - - const plugin = ApolloServerPluginLandingPageProductionDefault({ - footer: false, - }); - return plugin; - } + }, + }); + return plugin; }; /** @@ -72,11 +61,10 @@ export const getPluginBasedOnSandbox = async (sandbox: boolean, csrfToken: strin * - Initial document state * - Shared headers containing CSRF token */ -async function renderSandboxPage(service: GraphQLServerContext, isSandboxEnabled: () => boolean) { +async function renderSandboxPage(service: GraphQLServerContext) { const { getters } = await import('@app/store/index.js'); - const sandbox = isSandboxEnabled(); const csrfToken = getters.emhttp().var.csrfToken; - const plugin = await getPluginBasedOnSandbox(sandbox, csrfToken); + const plugin = await getSandboxPlugin(csrfToken); if (!plugin.serverWillStart) return preconditionFailed('serverWillStart'); const serverListener = await plugin.serverWillStart(service); @@ -88,15 +76,15 @@ async function renderSandboxPage(service: GraphQLServerContext, isSandboxEnabled } /** - * Apollo plugin to render the GraphQL Sandbox page on-demand based on current server state. + * Apollo plugin to render the GraphQL Sandbox page. * - * Usually, the `ApolloServerPluginLandingPageLocalDefault` plugin configures its - * parameters once, during server startup. This plugin defers the configuration - * and rendering to request-time instead of server startup. + * Access to this page is controlled by the sandbox-access-plugin which blocks + * GET requests when sandbox is disabled. This plugin only handles rendering + * the sandbox UI when it's allowed through. */ -export const createSandboxPlugin = (isSandboxEnabled: () => boolean): ApolloServerPlugin => ({ +export const createSandboxPlugin = (): ApolloServerPlugin => ({ serverWillStart: async (service) => ({ - renderLandingPage: () => renderSandboxPage(service, isSandboxEnabled), + renderLandingPage: () => renderSandboxPage(service), }) satisfies GraphQLServerListener, }); diff --git a/api/src/unraid-api/graph/utils/form-utils.ts b/api/src/unraid-api/graph/utils/form-utils.ts index 97a100475..f3f46cc91 100644 --- a/api/src/unraid-api/graph/utils/form-utils.ts +++ b/api/src/unraid-api/graph/utils/form-utils.ts @@ -1,5 +1,48 @@ import type { ControlElement, LabelElement, Layout, Rule } from '@jsonforms/core'; +/** + * Creates a simple VerticalLayout containing a Label followed by a Control element. + * Useful for detail views within array fields where UnraidSettingsLayout doesn't work well. + */ +export function createSimpleLabeledControl({ + scope, + label, + description, + controlOptions, + rule, +}: { + scope: string; + label: string; + description?: string; + controlOptions?: ControlElement['options']; + rule?: Rule; +}): Layout { + const layout: Layout = { + type: 'VerticalLayout', + elements: [ + { + type: 'Label', + text: label, + options: { + description, + }, + } as LabelElement, + { + type: 'Control', + scope: scope, + options: controlOptions, + } as ControlElement, + ], + }; + + // Add rule if provided + if (rule) { + layout.rule = rule; + } + + return layout; +} + /** * Creates a Layout (typically UnraidSettingsLayout) containing a Label and a Control element. */ @@ -44,3 +87,42 @@ export function createLabeledControl({ } return layout; } + +/** + * Creates an AccordionLayout that wraps child elements in an accordion interface. + * Each element becomes an accordion item with configurable titles and descriptions. + */ +export function createAccordionLayout({ + elements, + defaultOpen, + rule, +}: { + elements: Array< + Layout & { + options?: Layout['options'] & { + accordion?: { + title?: string; + description?: string; + }; + }; + } + >; + defaultOpen?: number | number[] | 'all'; + rule?: Rule; +}): Layout { + const layout: Layout = { + type: 'AccordionLayout', + options: { + accordion: { + defaultOpen, + }, + }, + elements, + }; + + if (rule) { + layout.rule = rule; + } + + return layout; +} diff --git a/api/src/unraid-api/main.ts b/api/src/unraid-api/main.ts index 3fb7471eb..4b753abfa 100644 --- a/api/src/unraid-api/main.ts +++ b/api/src/unraid-api/main.ts @@ -1,5 +1,6 @@ import type { NestFastifyApplication } from '@nestjs/platform-fastify'; import { ValidationPipe } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; import { NestFactory } from '@nestjs/core'; import { FastifyAdapter } from '@nestjs/platform-fastify/index.js'; @@ -75,6 +76,39 @@ export async function bootstrapNestServer(): Promise { hsts: false, }); + // Add sandbox access control hook + server.addHook('preHandler', async (request, reply) => { + // Only block GET requests to /graphql when sandbox is disabled + if (request.method === 'GET') { + // Extract pathname without query parameters + const urlPath = request.url.split('?')[0]; + + if (urlPath === '/graphql') { + const configService = app.get(ConfigService); + const sandboxValue = configService.get('api.sandbox'); + + // Robustly coerce to boolean - only true when explicitly true + const sandboxEnabled = + sandboxValue === true || + (typeof sandboxValue === 'string' && sandboxValue.toLowerCase() === 'true'); + + if (!sandboxEnabled) { + reply.status(403).send({ + errors: [ + { + message: 'GraphQL sandbox is disabled. Enable it in the API settings.', + extensions: { + code: 'SANDBOX_DISABLED', + }, + }, + ], + }); + return; + } + } + } + }); + // Allows all origins but still checks authentication app.enableCors({ origin: true, // Allows all origins diff --git a/api/src/unraid-api/rest/rest.controller.ts b/api/src/unraid-api/rest/rest.controller.ts index 93fcde924..1711ce698 100644 --- a/api/src/unraid-api/rest/rest.controller.ts +++ b/api/src/unraid-api/rest/rest.controller.ts @@ -1,17 +1,20 @@ -import { All, Controller, Get, Logger, Param, Req, Res } from '@nestjs/common'; +import { Controller, Get, Logger, Param, Query, Req, Res, UnauthorizedException } from '@nestjs/common'; import { Resource } from '@unraid/shared/graphql.model.js'; -import got from 'got'; import { AuthActionVerb, AuthPossession, UsePermissions } from 'nest-authz'; import type { FastifyReply, FastifyRequest } from '@app/unraid-api/types/fastify.js'; import { Public } from '@app/unraid-api/auth/public.decorator.js'; +import { OidcAuthService } from '@app/unraid-api/graph/resolvers/sso/oidc-auth.service.js'; import { RestService } from '@app/unraid-api/rest/rest.service.js'; @Controller() export class RestController { protected logger = new Logger(RestController.name); - constructor(private readonly restService: RestService) {} + constructor( + private readonly restService: RestService, + private readonly oidcAuthService: OidcAuthService + ) {} @Get('/') @Public() @@ -54,4 +57,113 @@ export class RestController { return res.status(500).send(`Error: Failed to get customizations`); } } + + @Get( + process.env.NODE_ENV === 'development' + ? ['/graphql/api/auth/oidc/authorize/:providerId', '/api/auth/oidc/authorize/:providerId'] + : ['/graphql/api/auth/oidc/authorize/:providerId'] + ) + @Public() + async oidcAuthorize( + @Param('providerId') providerId: string, + @Query('state') state: string, + @Req() req: FastifyRequest, + @Res() res: FastifyReply + ) { + try { + if (!state) { + return res.status(400).send('State parameter is required'); + } + + // Get the host from the request headers + const host = req.headers.host || undefined; + + const authUrl = await this.oidcAuthService.getAuthorizationUrl(providerId, state, host); + this.logger.log(`Redirecting to OIDC provider: ${authUrl}`); + + // Manually set redirect headers for better proxy compatibility + res.status(302); + res.header('Location', authUrl); + return res.send(); + } catch (error: unknown) { + this.logger.error(`OIDC authorize error for provider ${providerId}:`, error); + + // Log more details about the error + if (error instanceof Error) { + this.logger.error(`Error message: ${error.message}`); + if (error.stack) { + this.logger.debug(`Stack trace: ${error.stack}`); + } + } + + return res.status(400).send('Invalid provider or configuration'); + } + } + + @Get( + process.env.NODE_ENV === 'development' + ? ['/graphql/api/auth/oidc/callback', '/api/auth/oidc/callback'] + : ['/graphql/api/auth/oidc/callback'] + ) + @Public() + async oidcCallback( + @Query('code') code: string, + @Query('state') state: string, + @Req() req: FastifyRequest, + @Res() res: FastifyReply + ) { + try { + if (!code || !state) { + return res.status(400).send('Missing required parameters'); + } + + // Extract provider ID from state + const { providerId } = this.oidcAuthService.extractProviderFromState(state); + + // Get the full callback URL as received, respecting reverse proxy headers + const protocol = (req.headers['x-forwarded-proto'] as string) || req.protocol || 'http'; + const host = + (req.headers['x-forwarded-host'] as string) || req.headers.host || 'localhost:3000'; + const fullUrl = `${protocol}://${host}${req.url}`; + + this.logger.debug(`Full callback URL from request: ${fullUrl}`); + + const paddedToken = await this.oidcAuthService.handleCallback( + providerId, + code, + state, + host, + fullUrl + ); + + // Redirect to login page with the token in hash to keep it out of server logs + const loginUrl = `/login#token=${encodeURIComponent(paddedToken)}`; + + // Manually set redirect headers for better proxy compatibility + res.header('Cache-Control', 'no-store'); + res.header('Pragma', 'no-cache'); + res.header('Expires', '0'); + res.status(302); + res.header('Location', loginUrl); + return res.send(); + } catch (error: unknown) { + this.logger.error(`OIDC callback error: ${error}`); + + // Use a generic error message to avoid leaking sensitive information + const errorMessage = 'Authentication failed'; + + // Log detailed error for debugging but don't expose to user + if (error instanceof UnauthorizedException) { + this.logger.debug(`UnauthorizedException occurred during OIDC callback`); + } else if (error instanceof Error) { + this.logger.debug(`Error during OIDC callback: ${error.message}`); + } + + const loginUrl = `/login#error=${encodeURIComponent(errorMessage)}`; + + res.status(302); + res.header('Location', loginUrl); + return res.send(); + } + } } diff --git a/api/src/unraid-api/rest/rest.module.ts b/api/src/unraid-api/rest/rest.module.ts index 55df19811..a5e47265a 100644 --- a/api/src/unraid-api/rest/rest.module.ts +++ b/api/src/unraid-api/rest/rest.module.ts @@ -2,11 +2,12 @@ import { Module } from '@nestjs/common'; import { CliServicesModule } from '@app/unraid-api/cli/cli-services.module.js'; import { RCloneModule } from '@app/unraid-api/graph/resolvers/rclone/rclone.module.js'; +import { SsoModule } from '@app/unraid-api/graph/resolvers/sso/sso.module.js'; import { RestController } from '@app/unraid-api/rest/rest.controller.js'; import { RestService } from '@app/unraid-api/rest/rest.service.js'; @Module({ - imports: [RCloneModule, CliServicesModule], + imports: [RCloneModule, CliServicesModule, SsoModule], controllers: [RestController], providers: [RestService], }) diff --git a/packages/unraid-api-plugin-connect/src/__test__/cloud.service.test.ts b/packages/unraid-api-plugin-connect/src/__test__/cloud.service.test.ts index 335dc9b21..b01458f59 100644 --- a/packages/unraid-api-plugin-connect/src/__test__/cloud.service.test.ts +++ b/packages/unraid-api-plugin-connect/src/__test__/cloud.service.test.ts @@ -45,5 +45,5 @@ describe('CloudService.hardCheckCloud (integration)', () => { } throw error; } - }); + }, { timeout: 10000 }); }); diff --git a/packages/unraid-shared/package.json b/packages/unraid-shared/package.json index 8d5420609..5739a77ad 100644 --- a/packages/unraid-shared/package.json +++ b/packages/unraid-shared/package.json @@ -37,6 +37,7 @@ "@types/lodash-es": "4.17.12", "@types/node": "22.17.1", "@types/ws": "^8.5.13", + "class-transformer": "0.5.1", "class-validator": "0.14.2", "graphql": "16.11.0", "graphql-scalars": "1.24.2", diff --git a/packages/unraid-shared/src/services/user-settings.ts b/packages/unraid-shared/src/services/user-settings.ts index 75d3c3cd5..92dc7dda5 100644 --- a/packages/unraid-shared/src/services/user-settings.ts +++ b/packages/unraid-shared/src/services/user-settings.ts @@ -19,7 +19,7 @@ export interface SettingsFragment { getCurrentValues(): Promise; updateValues( values: Partial - ): Promise<{ restartRequired?: boolean; values: Partial }>; + ): Promise<{ restartRequired?: boolean; values: Partial; warnings?: string[] }>; } /** @@ -117,16 +117,17 @@ export class UserSettingsService { async updateValues( name: T, values: Partial - ): Promise<{ restartRequired?: boolean; values: Partial }> { + ): Promise<{ restartRequired?: boolean; values: Partial; warnings?: string[] }> { const fragment = this.getOrThrow(name); return fragment.updateValues(values); } /** Update values from a namespaced object. */ async updateNamespacedValues( - values: Record - ): Promise<{ restartRequired: boolean; values: Record }> { + values: Record + ): Promise<{ restartRequired: boolean; values: Record; warnings?: string[] }> { let restartRequired = false; + let allWarnings: string[] = []; for (const [key, fragmentValues] of Object.entries(values)) { if (!this.settings.has(key as keyof UserSettings)) { @@ -136,14 +137,26 @@ export class UserSettingsService { const result = await this.updateValues( key as keyof UserSettings, - fragmentValues + fragmentValues as Partial ); if (result.restartRequired) { restartRequired = true; } + // Collect any warnings from individual fragments + if (result.warnings) { + allWarnings = allWarnings.concat(result.warnings); + } } - return { restartRequired, values: await this.getAllValues() }; + const response: { restartRequired: boolean; values: Record; warnings?: string[] } = { + restartRequired, + values: await this.getAllValues() + }; + if (allWarnings.length > 0) { + response.warnings = allWarnings; + } + + return response; } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c7bcc487a..57a11c286 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -256,6 +256,9 @@ importers: node-window-polyfill: specifier: 1.0.4 version: 1.0.4 + openid-client: + specifier: ^6.6.2 + version: 6.6.2 p-retry: specifier: 6.2.1 version: 6.2.1 @@ -439,7 +442,7 @@ importers: version: 9.33.0(jiti@2.5.1) eslint-plugin-import: specifier: 2.32.0 - version: 2.32.0(@typescript-eslint/parser@8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.5.1)) + version: 2.32.0(@typescript-eslint/parser@8.39.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-typescript@4.4.4)(eslint@9.33.0(jiti@2.5.1)) eslint-plugin-no-relative-import-paths: specifier: 1.6.1 version: 1.6.1 @@ -755,6 +758,9 @@ importers: '@types/ws': specifier: ^8.5.13 version: 8.18.1 + class-transformer: + specifier: 0.5.1 + version: 0.5.1 class-validator: specifier: 0.14.2 version: 0.14.2 @@ -940,6 +946,9 @@ importers: '@vue/tsconfig': specifier: 0.7.0 version: 0.7.0(typescript@5.9.2)(vue@3.5.18(typescript@5.9.2)) + ajv: + specifier: 8.17.1 + version: 8.17.1 concurrently: specifier: 9.2.0 version: 9.2.0 @@ -1076,6 +1085,9 @@ importers: '@vueuse/integrations': specifier: 13.6.0 version: 13.6.0(change-case@5.4.4)(focus-trap@7.6.5)(fuse.js@7.1.0)(jwt-decode@4.0.0)(vue@3.5.18(typescript@5.9.2)) + ajv: + specifier: 8.17.1 + version: 8.17.1 class-variance-authority: specifier: 0.7.1 version: 0.7.1 @@ -10665,6 +10677,9 @@ packages: engines: {node: ^14.16.0 || >=16.10.0} hasBin: true + oauth4webapi@3.6.1: + resolution: {integrity: sha512-b39+drVyA4aNUptFOhkkmGWnG/BE7dT29SW/8PVYElqp7j/DBqzm5SS1G+MUD07XlTcBOAG+6Cb/35Cx2kHIuQ==} + object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -10749,6 +10764,9 @@ packages: resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==} hasBin: true + openid-client@6.6.2: + resolution: {integrity: sha512-Xya5TNMnnZuTM6DbHdB4q0S3ig2NTAELnii/ASie1xDEr8iiB8zZbO871OWBdrw++sd3hW6bqWjgcmSy1RTWHA==} + optimism@0.18.1: resolution: {integrity: sha512-mLXNwWPa9dgFyDqkNi54sjDyNJ9/fTI6WGBLgnXku1vdKY/jovHfZT5r+aiVeFFLOz+foPNOm5YJ4mqgld2GBQ==} @@ -24530,6 +24548,8 @@ snapshots: pkg-types: 2.2.0 tinyexec: 1.0.1 + oauth4webapi@3.6.1: {} + object-assign@4.1.1: {} object-inspect@1.13.4: {} @@ -24627,6 +24647,11 @@ snapshots: opener@1.5.2: {} + openid-client@6.6.2: + dependencies: + jose: 6.0.12 + oauth4webapi: 3.6.1 + optimism@0.18.1: dependencies: '@wry/caches': 1.0.1 diff --git a/unraid-ui/package.json b/unraid-ui/package.json index 83f9d46fb..645b240ec 100644 --- a/unraid-ui/package.json +++ b/unraid-ui/package.json @@ -43,7 +43,8 @@ }, "peerDependencies": { "tailwindcss": "4.1.11", - "vue": "3.5.18" + "vue": "3.5.18", + "ajv": "8.17.1" }, "dependencies": { "@headlessui/vue": "1.7.23", @@ -84,6 +85,7 @@ "@vitest/ui": "3.2.4", "@vue/test-utils": "2.4.6", "@vue/tsconfig": "0.7.0", + "ajv": "8.17.1", "concurrently": "9.2.0", "eslint": "9.33.0", "eslint-config-prettier": "10.1.8", diff --git a/unraid-ui/src/forms/AccordionLayout.vue b/unraid-ui/src/forms/AccordionLayout.vue new file mode 100644 index 000000000..a6354c704 --- /dev/null +++ b/unraid-ui/src/forms/AccordionLayout.vue @@ -0,0 +1,157 @@ + + + diff --git a/unraid-ui/src/forms/HorizontalLayout.vue b/unraid-ui/src/forms/HorizontalLayout.vue index 5a6bd372e..d502fdd9a 100644 --- a/unraid-ui/src/forms/HorizontalLayout.vue +++ b/unraid-ui/src/forms/HorizontalLayout.vue @@ -15,7 +15,7 @@ */ import { useJsonFormsVisibility } from '@/forms/composables/useJsonFormsVisibility'; -import type { HorizontalLayout } from '@jsonforms/core'; +import type { HorizontalLayout, UISchemaElement } from '@jsonforms/core'; import { DispatchRenderer, type RendererProps } from '@jsonforms/vue'; import { computed } from 'vue'; @@ -35,7 +35,7 @@ const elements = computed(() => {

i{!&4jjul-%rAOy00-KM0V=+6%b0-6HCS|@cuXJ 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�RWLG%HU+ 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Νc9W$;!7NYiM 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/(bk8b=ENB^WBV?0kjRZ%*Q+D6;4 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 F?_XBCND2S| literal 0 HcmV?d00001 diff --git a/api/docs/public/index.md b/api/docs/public/index.md index 35ac31144..28a0305e3 100644 --- a/api/docs/public/index.md +++ b/api/docs/public/index.md @@ -22,6 +22,7 @@ The API will be integrated directly into the Unraid operating system in an upcom - [CLI Commands](./cli.md) - Reference for all available command-line interface commands - [Using the Unraid API](./how-to-use-the-api.md) - Comprehensive guide on using the GraphQL API +- [OIDC Provider Setup](./oidc-provider-setup.md) - OIDC SSO provider configuration examples - [Upcoming Features](./upcoming-features.md) - Roadmap of planned features and improvements ## Key Features diff --git a/api/docs/public/oidc-provider-setup.md b/api/docs/public/oidc-provider-setup.md new file mode 100644 index 000000000..60de65df6 --- /dev/null +++ b/api/docs/public/oidc-provider-setup.md @@ -0,0 +1,402 @@ +--- +title: OIDC Provider Setup +description: Configure OIDC (OpenID Connect) providers for SSO authentication in Unraid API +sidebar_position: 3 +--- + +# OIDC Provider Setup + +This guide walks you through configuring OIDC (OpenID Connect) providers for SSO authentication in the Unraid API using the web interface. + +## Accessing OIDC Settings + +1. Navigate to your Unraid server's web interface +2. The OIDC Providers section is available on the main configuration page +3. You'll see tabs for different providers - click the **+** button to add a new provider + +### OIDC Providers Interface Overview + +![Login Page with SSO Options](./images/sso-with-options.png) +_Screenshot: Login page showing traditional login form with SSO options - "Login With Unraid.net" and "Sign in with Google" buttons_ + +The interface includes: + +- **Provider tabs**: Each configured provider (Unraid.net, Google, etc.) appears as a tab +- **Add Provider button**: Click the **+** button to add new providers +- **Authorization Mode dropdown**: Toggle between "simple" and "advanced" modes +- **Simple Authorization section**: Configure allowed email domains and specific addresses +- **Add Item buttons**: Click to add multiple authorization rules + +## Understanding Authorization Modes + +The interface provides two authorization modes: + +### Simple Mode (Recommended) + +Simple mode is the easiest way to configure authorization. You can: + +- Allow specific email domains (e.g., @company.com) +- Allow specific email addresses +- Configure who can access your Unraid server with minimal setup + +**When to use Simple Mode:** + +- You want to allow all users from your company domain +- You have a small list of specific users +- You're new to OIDC configuration + +