From 40910c9a375725a0634d6440fecdae88d89d3193 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Can=20Karag=C3=B6z?= Date: Tue, 7 Apr 2026 21:12:43 +0300 Subject: [PATCH 1/4] Web Docs Improvement --- Directory.Build.props | 4 ++-- .../Layout/MainLayout.razor | 2 +- ...ltimateAuth.EntityFrameworkCore.Bundle.csproj | 4 ++-- .../logo.png | Bin 3551 -> 0 bytes .../uauthlogo.png | Bin 0 -> 4954 bytes .../CodeBeam.UltimateAuth.InMemory.Bundle.csproj | 4 ++-- nuget/CodeBeam.UltimateAuth.InMemory/logo.png | Bin 3551 -> 0 bytes .../CodeBeam.UltimateAuth.InMemory/uauthlogo.png | Bin 0 -> 4954 bytes ...CodeBeam.UltimateAuth.Reference.Bundle.csproj | 4 ++-- .../logo.png | Bin 3551 -> 0 bytes .../uauthlogo.png | Bin 0 -> 4954 bytes .../CodeBeam.UltimateAuth.Core.csproj | 4 ++-- src/CodeBeam.UltimateAuth.Core/logo.png | Bin 3551 -> 0 bytes src/CodeBeam.UltimateAuth.Core/uauthlogo.png | Bin 0 -> 4954 bytes .../CodeBeam.UltimateAuth.Server.csproj | 4 ++-- src/CodeBeam.UltimateAuth.Server/logo.png | Bin 3551 -> 0 bytes src/CodeBeam.UltimateAuth.Server/uauthlogo.png | Bin 0 -> 4954 bytes ...uth.Authentication.EntityFrameworkCore.csproj | 4 ++-- .../logo.png | Bin 3551 -> 0 bytes .../uauthlogo.png | Bin 0 -> 4954 bytes ...m.UltimateAuth.Authentication.InMemory.csproj | 4 ++-- .../logo.png | Bin 3551 -> 0 bytes .../uauthlogo.png | Bin 0 -> 4954 bytes ...m.UltimateAuth.Authorization.Contracts.csproj | 4 ++-- .../logo.png | Bin 3551 -> 0 bytes .../uauthlogo.png | Bin 0 -> 4954 bytes ...Auth.Authorization.EntityFrameworkCore.csproj | 4 ++-- .../logo.png | Bin 3551 -> 0 bytes .../uauthlogo.png | Bin 0 -> 4954 bytes ...am.UltimateAuth.Authorization.InMemory.csproj | 4 ++-- .../logo.png | Bin 3551 -> 0 bytes .../uauthlogo.png | Bin 0 -> 4954 bytes ...m.UltimateAuth.Authorization.Reference.csproj | 4 ++-- .../logo.png | Bin 3551 -> 0 bytes .../uauthlogo.png | Bin 0 -> 4954 bytes .../CodeBeam.UltimateAuth.Authorization.csproj | 4 ++-- .../CodeBeam.UltimateAuth.Authorization/logo.png | Bin 3551 -> 0 bytes .../uauthlogo.png | Bin 0 -> 4954 bytes .../CodeBeam.UltimateAuth.Client.Blazor.csproj | 4 ++-- .../CodeBeam.UltimateAuth.Client.Blazor/logo.png | Bin 3551 -> 0 bytes .../uauthlogo.png | Bin 0 -> 4954 bytes .../CodeBeam.UltimateAuth.Client.csproj | 4 ++-- src/client/CodeBeam.UltimateAuth.Client/logo.png | Bin 3551 -> 0 bytes .../CodeBeam.UltimateAuth.Client/uauthlogo.png | Bin 0 -> 4954 bytes ...eam.UltimateAuth.Credentials.Contracts.csproj | 4 ++-- .../logo.png | Bin 3551 -> 0 bytes .../uauthlogo.png | Bin 0 -> 4954 bytes ...teAuth.Credentials.EntityFrameworkCore.csproj | 4 ++-- .../logo.png | Bin 3551 -> 0 bytes .../uauthlogo.png | Bin 0 -> 4954 bytes ...Beam.UltimateAuth.Credentials.InMemory.csproj | 4 ++-- .../logo.png | Bin 3551 -> 0 bytes .../uauthlogo.png | Bin 0 -> 4954 bytes ...eam.UltimateAuth.Credentials.Reference.csproj | 4 ++-- .../logo.png | Bin 3551 -> 0 bytes .../uauthlogo.png | Bin 0 -> 4954 bytes .../CodeBeam.UltimateAuth.Credentials.csproj | 4 ++-- .../CodeBeam.UltimateAuth.Credentials/logo.png | Bin 3551 -> 0 bytes .../uauthlogo.png | Bin 0 -> 4954 bytes ...eBeam.UltimateAuth.EntityFrameworkCore.csproj | 4 ++-- .../logo.png | Bin 3551 -> 0 bytes .../uauthlogo.png | Bin 0 -> 4954 bytes .../CodeBeam.UltimateAuth.InMemory.csproj | 4 ++-- .../CodeBeam.UltimateAuth.InMemory/logo.png | Bin 3551 -> 0 bytes .../CodeBeam.UltimateAuth.InMemory/uauthlogo.png | Bin 0 -> 4954 bytes .../CodeBeam.UltimateAuth.Policies.csproj | 4 ++-- .../CodeBeam.UltimateAuth.Policies/logo.png | Bin 3551 -> 0 bytes .../CodeBeam.UltimateAuth.Policies/uauthlogo.png | Bin 0 -> 4954 bytes .../CodeBeam.UltimateAuth.Security.Argon2.csproj | 4 ++-- .../logo.png | Bin 3551 -> 0 bytes .../uauthlogo.png | Bin 0 -> 4954 bytes ...imateAuth.Sessions.EntityFrameworkCore.csproj | 4 ++-- .../logo.png | Bin 3551 -> 0 bytes .../uauthlogo.png | Bin 0 -> 4954 bytes ...odeBeam.UltimateAuth.Sessions.InMemory.csproj | 4 ++-- .../logo.png | Bin 3551 -> 0 bytes .../uauthlogo.png | Bin 0 -> 4954 bytes ...ltimateAuth.Tokens.EntityFrameworkCore.csproj | 4 ++-- .../logo.png | Bin 3551 -> 0 bytes .../uauthlogo.png | Bin 0 -> 4954 bytes .../CodeBeam.UltimateAuth.Tokens.InMemory.csproj | 4 ++-- .../logo.png | Bin 3551 -> 0 bytes .../uauthlogo.png | Bin 0 -> 4954 bytes .../CodeBeam.UltimateAuth.Users.Contracts.csproj | 4 ++-- .../logo.png | Bin 3551 -> 0 bytes .../uauthlogo.png | Bin 0 -> 4954 bytes ...UltimateAuth.Users.EntityFrameworkCore.csproj | 4 ++-- .../logo.png | Bin 3551 -> 0 bytes .../uauthlogo.png | Bin 0 -> 4954 bytes .../CodeBeam.UltimateAuth.Users.InMemory.csproj | 4 ++-- .../logo.png | Bin 3551 -> 0 bytes .../uauthlogo.png | Bin 0 -> 4954 bytes .../CodeBeam.UltimateAuth.Users.Reference.csproj | 4 ++-- .../logo.png | Bin 3551 -> 0 bytes .../uauthlogo.png | Bin 0 -> 4954 bytes .../CodeBeam.UltimateAuth.Users.csproj | 4 ++-- src/users/CodeBeam.UltimateAuth.Users/logo.png | Bin 3551 -> 0 bytes .../CodeBeam.UltimateAuth.Users/uauthlogo.png | Bin 0 -> 4954 bytes 98 files changed, 67 insertions(+), 67 deletions(-) delete mode 100644 nuget/CodeBeam.UltimateAuth.EntityFrameworkCore/logo.png create mode 100644 nuget/CodeBeam.UltimateAuth.EntityFrameworkCore/uauthlogo.png delete mode 100644 nuget/CodeBeam.UltimateAuth.InMemory/logo.png create mode 100644 nuget/CodeBeam.UltimateAuth.InMemory/uauthlogo.png delete mode 100644 nuget/CodeBeam.UltimateAuth.Reference.Bundle/logo.png create mode 100644 nuget/CodeBeam.UltimateAuth.Reference.Bundle/uauthlogo.png delete mode 100644 src/CodeBeam.UltimateAuth.Core/logo.png create mode 100644 src/CodeBeam.UltimateAuth.Core/uauthlogo.png delete mode 100644 src/CodeBeam.UltimateAuth.Server/logo.png create mode 100644 src/CodeBeam.UltimateAuth.Server/uauthlogo.png delete mode 100644 src/authentication/CodeBeam.UltimateAuth.Authentication.EntityFrameworkCore/logo.png create mode 100644 src/authentication/CodeBeam.UltimateAuth.Authentication.EntityFrameworkCore/uauthlogo.png delete mode 100644 src/authentication/CodeBeam.UltimateAuth.Authentication.InMemory/logo.png create mode 100644 src/authentication/CodeBeam.UltimateAuth.Authentication.InMemory/uauthlogo.png delete mode 100644 src/authorization/CodeBeam.UltimateAuth.Authorization.Contracts/logo.png create mode 100644 src/authorization/CodeBeam.UltimateAuth.Authorization.Contracts/uauthlogo.png delete mode 100644 src/authorization/CodeBeam.UltimateAuth.Authorization.EntityFrameworkCore/logo.png create mode 100644 src/authorization/CodeBeam.UltimateAuth.Authorization.EntityFrameworkCore/uauthlogo.png delete mode 100644 src/authorization/CodeBeam.UltimateAuth.Authorization.InMemory/logo.png create mode 100644 src/authorization/CodeBeam.UltimateAuth.Authorization.InMemory/uauthlogo.png delete mode 100644 src/authorization/CodeBeam.UltimateAuth.Authorization.Reference/logo.png create mode 100644 src/authorization/CodeBeam.UltimateAuth.Authorization.Reference/uauthlogo.png delete mode 100644 src/authorization/CodeBeam.UltimateAuth.Authorization/logo.png create mode 100644 src/authorization/CodeBeam.UltimateAuth.Authorization/uauthlogo.png delete mode 100644 src/client/CodeBeam.UltimateAuth.Client.Blazor/logo.png create mode 100644 src/client/CodeBeam.UltimateAuth.Client.Blazor/uauthlogo.png delete mode 100644 src/client/CodeBeam.UltimateAuth.Client/logo.png create mode 100644 src/client/CodeBeam.UltimateAuth.Client/uauthlogo.png delete mode 100644 src/credentials/CodeBeam.UltimateAuth.Credentials.Contracts/logo.png create mode 100644 src/credentials/CodeBeam.UltimateAuth.Credentials.Contracts/uauthlogo.png delete mode 100644 src/credentials/CodeBeam.UltimateAuth.Credentials.EntityFrameworkCore/logo.png create mode 100644 src/credentials/CodeBeam.UltimateAuth.Credentials.EntityFrameworkCore/uauthlogo.png delete mode 100644 src/credentials/CodeBeam.UltimateAuth.Credentials.InMemory/logo.png create mode 100644 src/credentials/CodeBeam.UltimateAuth.Credentials.InMemory/uauthlogo.png delete mode 100644 src/credentials/CodeBeam.UltimateAuth.Credentials.Reference/logo.png create mode 100644 src/credentials/CodeBeam.UltimateAuth.Credentials.Reference/uauthlogo.png delete mode 100644 src/credentials/CodeBeam.UltimateAuth.Credentials/logo.png create mode 100644 src/credentials/CodeBeam.UltimateAuth.Credentials/uauthlogo.png delete mode 100644 src/persistence/CodeBeam.UltimateAuth.EntityFrameworkCore/logo.png create mode 100644 src/persistence/CodeBeam.UltimateAuth.EntityFrameworkCore/uauthlogo.png delete mode 100644 src/persistence/CodeBeam.UltimateAuth.InMemory/logo.png create mode 100644 src/persistence/CodeBeam.UltimateAuth.InMemory/uauthlogo.png delete mode 100644 src/policies/CodeBeam.UltimateAuth.Policies/logo.png create mode 100644 src/policies/CodeBeam.UltimateAuth.Policies/uauthlogo.png delete mode 100644 src/security/CodeBeam.UltimateAuth.Security.Argon2/logo.png create mode 100644 src/security/CodeBeam.UltimateAuth.Security.Argon2/uauthlogo.png delete mode 100644 src/sessions/CodeBeam.UltimateAuth.Sessions.EntityFrameworkCore/logo.png create mode 100644 src/sessions/CodeBeam.UltimateAuth.Sessions.EntityFrameworkCore/uauthlogo.png delete mode 100644 src/sessions/CodeBeam.UltimateAuth.Sessions.InMemory/logo.png create mode 100644 src/sessions/CodeBeam.UltimateAuth.Sessions.InMemory/uauthlogo.png delete mode 100644 src/tokens/CodeBeam.UltimateAuth.Tokens.EntityFrameworkCore/logo.png create mode 100644 src/tokens/CodeBeam.UltimateAuth.Tokens.EntityFrameworkCore/uauthlogo.png delete mode 100644 src/tokens/CodeBeam.UltimateAuth.Tokens.InMemory/logo.png create mode 100644 src/tokens/CodeBeam.UltimateAuth.Tokens.InMemory/uauthlogo.png delete mode 100644 src/users/CodeBeam.UltimateAuth.Users.Contracts/logo.png create mode 100644 src/users/CodeBeam.UltimateAuth.Users.Contracts/uauthlogo.png delete mode 100644 src/users/CodeBeam.UltimateAuth.Users.EntityFrameworkCore/logo.png create mode 100644 src/users/CodeBeam.UltimateAuth.Users.EntityFrameworkCore/uauthlogo.png delete mode 100644 src/users/CodeBeam.UltimateAuth.Users.InMemory/logo.png create mode 100644 src/users/CodeBeam.UltimateAuth.Users.InMemory/uauthlogo.png delete mode 100644 src/users/CodeBeam.UltimateAuth.Users.Reference/logo.png create mode 100644 src/users/CodeBeam.UltimateAuth.Users.Reference/uauthlogo.png delete mode 100644 src/users/CodeBeam.UltimateAuth.Users/logo.png create mode 100644 src/users/CodeBeam.UltimateAuth.Users/uauthlogo.png diff --git a/Directory.Build.props b/Directory.Build.props index 758c57e5..7b23949a 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,13 +1,13 @@ - 0.1.0-preview.1 + 0.1.0-preview.2 $(NoWarn);1591 CodeBeam CodeBeam https://github.com/CodeBeamOrg/UltimateAuth - https://github.com/CodeBeamOrg/UltimateAuth + https://ultimateauth.com Apache-2.0 true diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/MainLayout.razor b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/MainLayout.razor index 40ae601b..75526c72 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/MainLayout.razor +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/MainLayout.razor @@ -40,7 +40,7 @@ Home Docs - Samples + Samples Donate diff --git a/nuget/CodeBeam.UltimateAuth.EntityFrameworkCore/CodeBeam.UltimateAuth.EntityFrameworkCore.Bundle.csproj b/nuget/CodeBeam.UltimateAuth.EntityFrameworkCore/CodeBeam.UltimateAuth.EntityFrameworkCore.Bundle.csproj index 6b0ed53a..29faf8f7 100644 --- a/nuget/CodeBeam.UltimateAuth.EntityFrameworkCore/CodeBeam.UltimateAuth.EntityFrameworkCore.Bundle.csproj +++ b/nuget/CodeBeam.UltimateAuth.EntityFrameworkCore/CodeBeam.UltimateAuth.EntityFrameworkCore.Bundle.csproj @@ -13,7 +13,7 @@ authentication;authorization;identity;efcore;inmemory;bundle;auth-framework;security;jwt - logo.png + uauthlogo.png README.md @@ -28,7 +28,7 @@ - + diff --git a/nuget/CodeBeam.UltimateAuth.EntityFrameworkCore/logo.png b/nuget/CodeBeam.UltimateAuth.EntityFrameworkCore/logo.png deleted file mode 100644 index aa51a469d9a9fdd0ff8ea6c0711ae28dafc8fb3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3551 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 L597+M377u|47W$d diff --git a/nuget/CodeBeam.UltimateAuth.EntityFrameworkCore/uauthlogo.png b/nuget/CodeBeam.UltimateAuth.EntityFrameworkCore/uauthlogo.png new file mode 100644 index 0000000000000000000000000000000000000000..911f2530a1353be33b73719b23571bd6edfb6e96 GIT binary patch literal 4954 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 authentication;authorization;identity;efcore;inmemory;bundle;auth-framework;security;jwt - logo.png + uauthlogo.png README.md @@ -29,7 +29,7 @@ - + diff --git a/nuget/CodeBeam.UltimateAuth.InMemory/logo.png b/nuget/CodeBeam.UltimateAuth.InMemory/logo.png deleted file mode 100644 index aa51a469d9a9fdd0ff8ea6c0711ae28dafc8fb3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3551 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 L597+M377u|47W$d diff --git a/nuget/CodeBeam.UltimateAuth.InMemory/uauthlogo.png b/nuget/CodeBeam.UltimateAuth.InMemory/uauthlogo.png new file mode 100644 index 0000000000000000000000000000000000000000..911f2530a1353be33b73719b23571bd6edfb6e96 GIT binary patch literal 4954 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 authentication;authorization;identity;reference;bundle;modular;auth-framework;users;credentials;policies - logo.png + uauthlogo.png README.md @@ -26,7 +26,7 @@ - + diff --git a/nuget/CodeBeam.UltimateAuth.Reference.Bundle/logo.png b/nuget/CodeBeam.UltimateAuth.Reference.Bundle/logo.png deleted file mode 100644 index aa51a469d9a9fdd0ff8ea6c0711ae28dafc8fb3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3551 zcmc&Xc{tSF_xCdxBiqO_iq{O4lwu2DSb;A%=F=K0+c{+32sv-c&N$~BD)Y9%>?&y> zYGCoVO#?RugJtI6?Oj4Xikq_##eExOu8#z~sJ`qwn_V95fXleF?#0s0`#q&cntE&{ zK55kr=c5yGn?Vnb{ahD{i-&@Oj@29x{I~| z%Rk>{!TK(Pw$CBSFiRt)Rdhn7l#WkGuW&mn%)<*8=ft-4vFy6s+vBM@AfTpm9=#I# z_ir~V$)f(``KpAok7R%?KMxzKZi%>38gID@G!89Uq{gUNdTfWiu4=4PtnU9R1*`2f ze#5fNLm&R(75n_%#6E7_D$h1J3cfn{={y@=kccUx7S|%P)qJDKt1tuL6 z4{HXr+cB&HWu=Ed-YV*f_?bc23iM<58(B&6pwre^i{SPTD6s}D(vHYh!l()6oQ8rA zH~~l!yb{8eeKeO_o;!&bhCNgj6dA%mXet28I{;`w0M3E`jw*fC=H(Le%r#``^w+V; zcGZt_zu)w#nwipjvS9P5LAz2C#zE{~cK0>fr@i`}UKKU<+|%dlL|@dmS1fib_mjvo zPSUj3W%&}QVtWvMQ0kA=*Y0`fTg2R%8tdidv|UC^&AU8(4lFjbWvbgyOc)2KNpsz6 z#gv?I_toDkJS)8W03Qj`pDw>rT^b(J{A50P>rQuhP<~LXA%cpSbH}4sT;9|UI$)$= z80@c)g@UQ#prNI|G!$T%hj?@2=Cn1?tKO@RNC60YyVW04!T7#IeTXke05$NJuLqac zX!UCBgJCf6I+C4I0{0J&{InU;Z{PjW^jo`q<|Q*!Q&%+H*yuJ0Iq zKYMY#-@)kDa{7^VpP(JTh@|@9gK@j5mTR8}e3vazM3n!<;WPKu-oT>QxYK!=-F_Lj zJ4(8zJ1+6&%kP)&4NBP15C5S}kvA}5&ojFc3tHa!9dnt_Lc$?}^T(l~EN2u13=aWt zRR&P~gu$TVzbO3sV@#s0xxUQC0mJ!j0bvQBkNrV+tI03%k$r{%5*wBk-W}<%css!J z5=UmK%qCxRCCwk7%OXgp#HKC_3jG>v4;oMgV&MG)Jc|=`h-_xdM<=sLci;Y5iV1%8 zFAJr7#X~@mnczomAxvy}!^We$|Ig^yjGauxsh*o5luu#WW;xWtmC=^0zlu%P#*8NC z(kT74pTCZDx>^CnqJ+DB!M8ryM@&04yvb93AJoW;nX30@Y31eaVM*l9X{MdQ*WWqv8l2+O~Bg3TYlN;#aT@JLXy5AopL zF-0i|$>>PdUU4iF@gvK7%b2z&;y>vj1K{3Ns#b^A<0oBp0fi3{8%Ud$&h8k|Uj5!f zh$|c67&6`2xcahv{@C3h8_PsnAgpf|aWiI8C?Gqa53>=Im#-IV^Bq~qwF$Li&vpQ{ zH@+tQGbzY>wZFDy*#Vpm0_rp{4??0LWcq->E_cP6NgVbzlN_xX(;KgGT@9NMiKysV?5pKJn0Hs--I6f{nOg!sU_}alo$0^APDUCoHQyaD0F2$uKGKLal5Am zLbXjt2UGvzk1zDQ{b6dZHN`f0GH%f{X?B5dz?nHFN0FzSF2pXXt0WAj#E!yF1$^Mr zqmf`+fxt!K-vs%OeEI)Dxd^IWu;F0r1adV~76d;%0&Dh85{@hfgB>5^&A#o!T^=l5J9n@xYn<1|^yrsml!Ne-3ol7+ z9HY;8^!4KtU)91t_TIk`UOk(AqP}vcrY7s#a3{OJVJ5kyrNO|-t)ChI=$J*gWw@V5oaJ~r7Or_sR7M(0zd56w)T$aW;@`8r<{FZCe{Cqw zlze}Y7Z4vj7uFN*%AEG5eoYlk&0QO_SRXQ?v5n6Dy3~De1#&LU(BU!myf2`vG)#S4 zm@pa0E^uh^tC-W=k)|yqPrf&*SscDu+~p|M9Uha5AsHRTO*F1-n{+_WT$^bex!pL# z{|$0tv^0HGK{2J{5sibs-1Pj_{*b>w&B*Re1c6m&ts^2Lum$YF=Tf`g%CvYMU3d7Ti(wX7kU!8wqJW`T*VFqzj5ru#pS8SY?%U zr7$K%TYXPIVAO}edyYRa@gOI?%|F#xLBGmWAyyoBhyqgu-BnEPDK;3y5zizq7@$9Azlg z=i1bA)HOV4HH#2NKnH{Ne~kKN^3q>|5Z6fWAC-C-=_IuIU{~XLecm4R%_(Aq?~i$D48BbU zS}`RG%kSSHF5OIz0ti)VZP*@@y@O81&O{t&BlAc29(@iDy zXY&OHs-Sj^G~9D^r%BwdhCr42geey2bBK18@h+j)SNU{W>>Ggne8Fd`-Od+8S)mBK zxoL%X*lH1zm^{S$4G;V``pZD0+#98}lPbGl`TMLf@^TarVN3aVYNQ;+R@m~lvf9H+ z=IH~;#EEDr-kA7=s_(5n#{9?EI<@o=nO-Y?Hx7Joi>H#rt`=Q@J{fBEckG!;b4Gbn z|MaiDhCR?Wj^2s~&yV|h->4+NR+~A}i@K&vV?(CU!358^JJ|XPD`Cs)h*+0Gfl~9) zL3Me9;V)||0S8M0yl89}Qz#WCo0!!soG0@|j37r-imY0U+5X%Z&!7nW^b4TCoHps8}MDB1EpzTAwfZPCL)JtjlrHa6C}uXN4L(9Trcu;xP0{ibHTV zPmPOK!GiE8`PBok*BYE??%U+xa1YBdt4rJ4{r!;o037zV5paVTj<=7=lpz zVUTnIbbWimND?tS#8==Hq!|cbT7G}Um2{{shjqT4C<_rjEssSHtw@g9uX_rO#0FvE zjLUOfNNll%Oe$1_DJyI4snb+7g^G&K+l0QHe$0tp*F(pVlQH1Psfb&uq$vj^#MtZg z{>tsHYz-L3pI$bAOs{(riqj^XJwFF>z$7iLs%F1QPboPY8gj9ct^Ml?zwLBOTjrZPFZU L597+M377u|47W$d diff --git a/nuget/CodeBeam.UltimateAuth.Reference.Bundle/uauthlogo.png b/nuget/CodeBeam.UltimateAuth.Reference.Bundle/uauthlogo.png new file mode 100644 index 0000000000000000000000000000000000000000..911f2530a1353be33b73719b23571bd6edfb6e96 GIT binary patch literal 4954 zcmcgwWmFVEw8j;Xg(a76mW8EDLILSmU};dg1$nfzN~|mb0>UDKq##I2hf7FENJvPt zbgXnOpwe&s`ObOq@BNs$_sq;W_s-n8-*;!?j106X$yv#Xh=?e4;2I_bdHAo9krH|( z^Q{zu0Q$l${fUSu=>D}^V+-MzL_}baj)tmPNcLWiz9+pl>nr8aU3V2q?ymO^#*<)B?F#;?x@e z)9+HyW~+1-H>^+21!UDMn43PhYim1ss+97s&v|z{_gPU{`}uacB^V5LiuYr&7KZ%Y zj%}CMbjfWXr5Ov)jqUTIWr=IAd|3Ewaw=@teA$mBs_;Qg{X)|fkJ@|zX@;gt31kx8 zYLy<}u_i_8{>8*f4Z?k#@!Bi~8>WC2I>aQXVn;Mw{N}<1d2o*O;bw);ifkWEFe2Az z{|LX0MGD+o&H69uv>uZ8!NO7Z9$$7_0@voyIQ*Np#w3i?p>#2;s@vp5pnZ)GbMR*_p^5kW;p(5gU=xwOPizDcSd0L(2jk2RMw)n zi3{|3K+$8*Qew`hq7UdyonxPW3|d4^CDY6JOaSU`Fbxey=n40>Ptk2nB%M$B zlA)rlgPWH9`~*YGl6Q!@AFrIxW{N0V^Xts1WV%~%BX+E5{^_q0&lfAeOrF$?rdlgH zNZaSXZQCP?F9QzJ@PYG+#Pq)dd}^^`bfjdNoLsD_YYnOueN|$%w29qm87~+sN4ibK z80Sdk^kL#tn}_|{p*nWlV+pqaSD-6>l5;^Vk3mE;YhuSy^FbKXv)f8%cG%yUcvEV* zV>~)MUFtEsU-g5{O~1k8Z#0nc_VA0Sy{u~Dop1eQiq2uB0ZTWQ5sG=Ig?5ZX*Y?H$P_!;}T!_9t` zF6Z^E!>ueY!b)3=T(e;n)h`)v%;+ZrD=L@3;h@LkN{oZ%hiB;+l($x_J}qO$^h7@h zMI)EjfeHbU@OL{4Nbqo~BY{WE zB!~!}Wu|sg6LOV*3@f;CN~aP+IqkqG#AlGEn=d>4Bn3WD0ADDF0`|TK<`uD!gHtZ^ zJ4k35bm!XdZXZ5Y2n0;m9ZWF&jALzP4<3`J$8a0rj2_8<0WA~jz!CT;0F%l==}6T5 zab_+Pj9m&f{ftz}1p}706tf5@Gbg@9(g=F_kcVzNaD$Shkt{8K;Cj9sFw&J)PMDiU zil%(peKK9vFrW*4|3;0>y1raAF$~S|P2C zYnz?(am`z9|eu;IG) z^#{hUjTUHGO!}|`Sp<+s8o0!aGD?97zLXG!o_tMeNPLO7{WZ*()j3x%;R(g{8wxL; zEno6IW=rx(obAN$r^e&}fokU;ADqK0z0*E;krI3u`=88z?|VuUwAXy*9y65uNpyN2 zKVJaJ>tNinNX{KHqprJ<3BJ;u;! zd$Py^REEuG~?uGko+0-e@##uwc3eVNVqyuk96B5!|aM1 z7nxzf2f**&`(1iO!XY0ab#8*B1paUjVtdoU$5Y-~$ZoWpq7vGL`j^u5|Mz|R|5(yX zf#gcMT2T=u3+>(Px-&cvbR)LAUKJjg{>P(}jWi$E8jF-GEt|)-zxrz}6S!2WN*;FF zfIStSnp2N`?eWLEsI4}nlVa*1!_@T2Wbt%E%a&Y1GKBddNTAu$#SX)FSQ~&mSiFfI zf3ixv8AD(_LoeMw+ksPAo2eZO{;Eq(5oLCZ25h6ClO-rXkUP5YF}6Q}^onZA~%OP+1L6dPRLDkq9XoS^d~S z0B6C1pWi*A*rRoL@|N;%J|VB0L)9Pg>^(Jb5P|J&{SrXTe|r>< zj}v&Yd8tz@B;m* zUs)zAIdbYH%)qXQ6Uj;4hy4ZJD{IZ9w$<8>zh(-UtuWk$pRR#sv4ezi69N!6(= z8vpXTFjKyOYuo8=W9i>RF>WJ?(K#5~@Aj;7`5@sHFj$o&g+s$sa=f$TW!0JguYtAV z2#p&`$`jrb7{g|a=AFvzBQ=N8EZuU(iKDPfgz^1SrIZAIF=A&MqLHs<*CbS)^X7eT zcsc5><&FZrl`_gW^xW|gR_2jRu~C{o_;DPke|_?$-R0cluf%!+?*7b1OZbg}*TmV> zG}CMMY|?`=6Uce7?qSE!Qh4f4SK?p%*9fCU`BD4DF~vV0+pjh{YF=6ue^)?CHgC&w zX}MKWmv^>JLf#(O664FtJR^}`!=iUjWjY6&M?+laTCW3fI~MH)-CMrvIppVYLgpr` z+LY;QLW_(++R{evK9LODUMrF7CT}dOk4&Nq$*S9#t~7ee^&Z{3L2jJ(g9uP0{2_)< z7ymak!zwU9QcW4CH2){(Tx2Y1N49B0Z=Q zGv=MWTrF=kX>?oOdN0Dl?Q8sMD`08??w+S^NF8)mUucq4+S+#KkM1c*F$XFTl_0{< zpT&;7>&N;xFDsX6*Lk81-AWY4`Z)GS`craS>Zy}}I8Hjfgo3-VH}Cu6ua~jCm#%_e zJDdb0-*~S4y3;QC?hAqe{*uN5#Mf(+&^oD6uM(P<8j9RwC53Hm1dB(Jl2E2ZpvETUE)M`Z$OA z0-0qgzh$v(2jm>M$j&24i-f*vZS#7k7lk+An-L6nKm%?_3G|h>RZI=$?@a0dmb^BXC?3oZQSA?hzLO5X+4HG{M)Mw;A;!hjD|a2UT<$92^kV>qT^g&SgU2)93R7MY zi&UkCDL4vqv+2p@q&4eOWgC3q8t@?1b|5L@JsO~1bY0kzjk-7+Yn-mfJ1P5Y;>op_ zdu>{R>l_8gl9Ob-+)Ow5_S8* zQXf%jFEg$Pq)-mqVJQfzDr}s?ODM23JzxCp$~pS46PUWW&O^^Fm?h@rP@EmOxEHy;T<6bO_pnnliH&qVCedzoGo`&CM z@0GUNaYA7?8q*VCv1&_HS7P+4wR}IJYI`O)y|=H?bR(PsKKt{pI(Yfo;cluMva>^| zQdy~%!%G`z--_&z#2RZAgO)(`ZSI^g7hdAmZ4Bl#WC`|WVuM= zhmg;El5d3E*=aPKoTIO5`0kYy*7zOI7$UR+d_Rlqg-rHN5LZH(z%SQS^_;FantmXV zAMMJRBb?AT>q5T31Nqa{O6~2OqAu!%FqPJicQNo^5h;ZSLg|@@ON`9E~R8S>OC(HGiEN(ToR{?RiSdi!$K^&KIr__^@v)#f>>S13Zwwc zld}A<;70{vj{`d(Q}!Gw0EM=rkHl;*EOR-{z4~=b{r#TQpO5GP$Cfi-1YdS;Q;!1y zLzFJ~f6!Pkqkof+FEdJgF*w+JyZ4<+$^r*_i{5x<1_6jQzJ$svHk) zXYu%T3h}>zOaxx{Arj1-^Beq`$=QtG;A5g}`4e4-(A=k?ulhMr_1$kna)L`T?8i=y zJ|1!Hx;zk7%PeAavykDds+R&xgOhsg{g}W!S_!ZFv@2q>M$>M${Y(zY8gIn-p^`H? z61{vsao}}Zw3fHJdGQbTpFp4hQ{3IXl*U{eVuRRVnbU5MzSInqhN#_B4ihhpCwH32 z6>|vRO>Benv=M<)uk{u07`yB;MHc>TwnCTX$nQY#5tKsryrOqKTL_Core domain primitives and abstractions for UltimateAuth. This package is not intended to be installed directly in most applications. Use CodeBeam.UltimateAuth.Server or Client packages instead. authentication;authorization;identity;security;oauth;login;session;auth;refresh-token;pkce;dotnet;aspnetcore;blazor;maui;auth-framework - logo.png + uauthlogo.png README.md @@ -19,7 +19,7 @@ - + diff --git a/src/CodeBeam.UltimateAuth.Core/logo.png b/src/CodeBeam.UltimateAuth.Core/logo.png deleted file mode 100644 index aa51a469d9a9fdd0ff8ea6c0711ae28dafc8fb3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3551 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 L597+M377u|47W$d diff --git a/src/CodeBeam.UltimateAuth.Core/uauthlogo.png b/src/CodeBeam.UltimateAuth.Core/uauthlogo.png new file mode 100644 index 0000000000000000000000000000000000000000..911f2530a1353be33b73719b23571bd6edfb6e96 GIT binary patch literal 4954 zcmcgwWmFVEw8j;Xg(a76mW8EDLILSmU};dg1$nfzN~|mb0>UDKq##I2hf7FENJvPt zbgXnOpwe&s`ObOq@BNs$_sq;W_s-n8-*;!?j106X$yv#Xh=?e4;2I_bdHAo9krH|( z^Q{zu0Q$l${fUSu=>D}^V+-MzL_}baj)tmPNcLWiz9+pl>nr8aU3V2q?ymO^#*<)B?F#;?x@e z)9+HyW~+1-H>^+21!UDMn43PhYim1ss+97s&v|z{_gPU{`}uacB^V5LiuYr&7KZ%Y zj%}CMbjfWXr5Ov)jqUTIWr=IAd|3Ewaw=@teA$mBs_;Qg{X)|fkJ@|zX@;gt31kx8 zYLy<}u_i_8{>8*f4Z?k#@!Bi~8>WC2I>aQXVn;Mw{N}<1d2o*O;bw);ifkWEFe2Az z{|LX0MGD+o&H69uv>uZ8!NO7Z9$$7_0@voyIQ*Np#w3i?p>#2;s@vp5pnZ)GbMR*_p^5kW;p(5gU=xwOPizDcSd0L(2jk2RMw)n zi3{|3K+$8*Qew`hq7UdyonxPW3|d4^CDY6JOaSU`Fbxey=n40>Ptk2nB%M$B zlA)rlgPWH9`~*YGl6Q!@AFrIxW{N0V^Xts1WV%~%BX+E5{^_q0&lfAeOrF$?rdlgH zNZaSXZQCP?F9QzJ@PYG+#Pq)dd}^^`bfjdNoLsD_YYnOueN|$%w29qm87~+sN4ibK z80Sdk^kL#tn}_|{p*nWlV+pqaSD-6>l5;^Vk3mE;YhuSy^FbKXv)f8%cG%yUcvEV* zV>~)MUFtEsU-g5{O~1k8Z#0nc_VA0Sy{u~Dop1eQiq2uB0ZTWQ5sG=Ig?5ZX*Y?H$P_!;}T!_9t` zF6Z^E!>ueY!b)3=T(e;n)h`)v%;+ZrD=L@3;h@LkN{oZ%hiB;+l($x_J}qO$^h7@h zMI)EjfeHbU@OL{4Nbqo~BY{WE zB!~!}Wu|sg6LOV*3@f;CN~aP+IqkqG#AlGEn=d>4Bn3WD0ADDF0`|TK<`uD!gHtZ^ zJ4k35bm!XdZXZ5Y2n0;m9ZWF&jALzP4<3`J$8a0rj2_8<0WA~jz!CT;0F%l==}6T5 zab_+Pj9m&f{ftz}1p}706tf5@Gbg@9(g=F_kcVzNaD$Shkt{8K;Cj9sFw&J)PMDiU zil%(peKK9vFrW*4|3;0>y1raAF$~S|P2C zYnz?(am`z9|eu;IG) z^#{hUjTUHGO!}|`Sp<+s8o0!aGD?97zLXG!o_tMeNPLO7{WZ*()j3x%;R(g{8wxL; zEno6IW=rx(obAN$r^e&}fokU;ADqK0z0*E;krI3u`=88z?|VuUwAXy*9y65uNpyN2 zKVJaJ>tNinNX{KHqprJ<3BJ;u;! zd$Py^REEuG~?uGko+0-e@##uwc3eVNVqyuk96B5!|aM1 z7nxzf2f**&`(1iO!XY0ab#8*B1paUjVtdoU$5Y-~$ZoWpq7vGL`j^u5|Mz|R|5(yX zf#gcMT2T=u3+>(Px-&cvbR)LAUKJjg{>P(}jWi$E8jF-GEt|)-zxrz}6S!2WN*;FF zfIStSnp2N`?eWLEsI4}nlVa*1!_@T2Wbt%E%a&Y1GKBddNTAu$#SX)FSQ~&mSiFfI zf3ixv8AD(_LoeMw+ksPAo2eZO{;Eq(5oLCZ25h6ClO-rXkUP5YF}6Q}^onZA~%OP+1L6dPRLDkq9XoS^d~S z0B6C1pWi*A*rRoL@|N;%J|VB0L)9Pg>^(Jb5P|J&{SrXTe|r>< zj}v&Yd8tz@B;m* zUs)zAIdbYH%)qXQ6Uj;4hy4ZJD{IZ9w$<8>zh(-UtuWk$pRR#sv4ezi69N!6(= z8vpXTFjKyOYuo8=W9i>RF>WJ?(K#5~@Aj;7`5@sHFj$o&g+s$sa=f$TW!0JguYtAV z2#p&`$`jrb7{g|a=AFvzBQ=N8EZuU(iKDPfgz^1SrIZAIF=A&MqLHs<*CbS)^X7eT zcsc5><&FZrl`_gW^xW|gR_2jRu~C{o_;DPke|_?$-R0cluf%!+?*7b1OZbg}*TmV> zG}CMMY|?`=6Uce7?qSE!Qh4f4SK?p%*9fCU`BD4DF~vV0+pjh{YF=6ue^)?CHgC&w zX}MKWmv^>JLf#(O664FtJR^}`!=iUjWjY6&M?+laTCW3fI~MH)-CMrvIppVYLgpr` z+LY;QLW_(++R{evK9LODUMrF7CT}dOk4&Nq$*S9#t~7ee^&Z{3L2jJ(g9uP0{2_)< z7ymak!zwU9QcW4CH2){(Tx2Y1N49B0Z=Q zGv=MWTrF=kX>?oOdN0Dl?Q8sMD`08??w+S^NF8)mUucq4+S+#KkM1c*F$XFTl_0{< zpT&;7>&N;xFDsX6*Lk81-AWY4`Z)GS`craS>Zy}}I8Hjfgo3-VH}Cu6ua~jCm#%_e zJDdb0-*~S4y3;QC?hAqe{*uN5#Mf(+&^oD6uM(P<8j9RwC53Hm1dB(Jl2E2ZpvETUE)M`Z$OA z0-0qgzh$v(2jm>M$j&24i-f*vZS#7k7lk+An-L6nKm%?_3G|h>RZI=$?@a0dmb^BXC?3oZQSA?hzLO5X+4HG{M)Mw;A;!hjD|a2UT<$92^kV>qT^g&SgU2)93R7MY zi&UkCDL4vqv+2p@q&4eOWgC3q8t@?1b|5L@JsO~1bY0kzjk-7+Yn-mfJ1P5Y;>op_ zdu>{R>l_8gl9Ob-+)Ow5_S8* zQXf%jFEg$Pq)-mqVJQfzDr}s?ODM23JzxCp$~pS46PUWW&O^^Fm?h@rP@EmOxEHy;T<6bO_pnnliH&qVCedzoGo`&CM z@0GUNaYA7?8q*VCv1&_HS7P+4wR}IJYI`O)y|=H?bR(PsKKt{pI(Yfo;cluMva>^| zQdy~%!%G`z--_&z#2RZAgO)(`ZSI^g7hdAmZ4Bl#WC`|WVuM= zhmg;El5d3E*=aPKoTIO5`0kYy*7zOI7$UR+d_Rlqg-rHN5LZH(z%SQS^_;FantmXV zAMMJRBb?AT>q5T31Nqa{O6~2OqAu!%FqPJicQNo^5h;ZSLg|@@ON`9E~R8S>OC(HGiEN(ToR{?RiSdi!$K^&KIr__^@v)#f>>S13Zwwc zld}A<;70{vj{`d(Q}!Gw0EM=rkHl;*EOR-{z4~=b{r#TQpO5GP$Cfi-1YdS;Q;!1y zLzFJ~f6!Pkqkof+FEdJgF*w+JyZ4<+$^r*_i{5x<1_6jQzJ$svHk) zXYu%T3h}>zOaxx{Arj1-^Beq`$=QtG;A5g}`4e4-(A=k?ulhMr_1$kna)L`T?8i=y zJ|1!Hx;zk7%PeAavykDds+R&xgOhsg{g}W!S_!ZFv@2q>M$>M${Y(zY8gIn-p^`H? z61{vsao}}Zw3fHJdGQbTpFp4hQ{3IXl*U{eVuRRVnbU5MzSInqhN#_B4ihhpCwH32 z6>|vRO>Benv=M<)uk{u07`yB;MHc>TwnCTX$nQY#5tKsryrOqKTL_ authentication;authorization;identity;security;server;oauth;pkce;jwt;aspnetcore;auth-framework - logo.png + uauthlogo.png README.md @@ -34,7 +34,7 @@ - + diff --git a/src/CodeBeam.UltimateAuth.Server/logo.png b/src/CodeBeam.UltimateAuth.Server/logo.png deleted file mode 100644 index aa51a469d9a9fdd0ff8ea6c0711ae28dafc8fb3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3551 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 L597+M377u|47W$d diff --git a/src/CodeBeam.UltimateAuth.Server/uauthlogo.png b/src/CodeBeam.UltimateAuth.Server/uauthlogo.png new file mode 100644 index 0000000000000000000000000000000000000000..911f2530a1353be33b73719b23571bd6edfb6e96 GIT binary patch literal 4954 zcmcgwWmFVEw8j;Xg(a76mW8EDLILSmU};dg1$nfzN~|mb0>UDKq##I2hf7FENJvPt zbgXnOpwe&s`ObOq@BNs$_sq;W_s-n8-*;!?j106X$yv#Xh=?e4;2I_bdHAo9krH|( z^Q{zu0Q$l${fUSu=>D}^V+-MzL_}baj)tmPNcLWiz9+pl>nr8aU3V2q?ymO^#*<)B?F#;?x@e z)9+HyW~+1-H>^+21!UDMn43PhYim1ss+97s&v|z{_gPU{`}uacB^V5LiuYr&7KZ%Y zj%}CMbjfWXr5Ov)jqUTIWr=IAd|3Ewaw=@teA$mBs_;Qg{X)|fkJ@|zX@;gt31kx8 zYLy<}u_i_8{>8*f4Z?k#@!Bi~8>WC2I>aQXVn;Mw{N}<1d2o*O;bw);ifkWEFe2Az z{|LX0MGD+o&H69uv>uZ8!NO7Z9$$7_0@voyIQ*Np#w3i?p>#2;s@vp5pnZ)GbMR*_p^5kW;p(5gU=xwOPizDcSd0L(2jk2RMw)n zi3{|3K+$8*Qew`hq7UdyonxPW3|d4^CDY6JOaSU`Fbxey=n40>Ptk2nB%M$B zlA)rlgPWH9`~*YGl6Q!@AFrIxW{N0V^Xts1WV%~%BX+E5{^_q0&lfAeOrF$?rdlgH zNZaSXZQCP?F9QzJ@PYG+#Pq)dd}^^`bfjdNoLsD_YYnOueN|$%w29qm87~+sN4ibK z80Sdk^kL#tn}_|{p*nWlV+pqaSD-6>l5;^Vk3mE;YhuSy^FbKXv)f8%cG%yUcvEV* zV>~)MUFtEsU-g5{O~1k8Z#0nc_VA0Sy{u~Dop1eQiq2uB0ZTWQ5sG=Ig?5ZX*Y?H$P_!;}T!_9t` zF6Z^E!>ueY!b)3=T(e;n)h`)v%;+ZrD=L@3;h@LkN{oZ%hiB;+l($x_J}qO$^h7@h zMI)EjfeHbU@OL{4Nbqo~BY{WE zB!~!}Wu|sg6LOV*3@f;CN~aP+IqkqG#AlGEn=d>4Bn3WD0ADDF0`|TK<`uD!gHtZ^ zJ4k35bm!XdZXZ5Y2n0;m9ZWF&jALzP4<3`J$8a0rj2_8<0WA~jz!CT;0F%l==}6T5 zab_+Pj9m&f{ftz}1p}706tf5@Gbg@9(g=F_kcVzNaD$Shkt{8K;Cj9sFw&J)PMDiU zil%(peKK9vFrW*4|3;0>y1raAF$~S|P2C zYnz?(am`z9|eu;IG) z^#{hUjTUHGO!}|`Sp<+s8o0!aGD?97zLXG!o_tMeNPLO7{WZ*()j3x%;R(g{8wxL; zEno6IW=rx(obAN$r^e&}fokU;ADqK0z0*E;krI3u`=88z?|VuUwAXy*9y65uNpyN2 zKVJaJ>tNinNX{KHqprJ<3BJ;u;! zd$Py^REEuG~?uGko+0-e@##uwc3eVNVqyuk96B5!|aM1 z7nxzf2f**&`(1iO!XY0ab#8*B1paUjVtdoU$5Y-~$ZoWpq7vGL`j^u5|Mz|R|5(yX zf#gcMT2T=u3+>(Px-&cvbR)LAUKJjg{>P(}jWi$E8jF-GEt|)-zxrz}6S!2WN*;FF zfIStSnp2N`?eWLEsI4}nlVa*1!_@T2Wbt%E%a&Y1GKBddNTAu$#SX)FSQ~&mSiFfI zf3ixv8AD(_LoeMw+ksPAo2eZO{;Eq(5oLCZ25h6ClO-rXkUP5YF}6Q}^onZA~%OP+1L6dPRLDkq9XoS^d~S z0B6C1pWi*A*rRoL@|N;%J|VB0L)9Pg>^(Jb5P|J&{SrXTe|r>< zj}v&Yd8tz@B;m* zUs)zAIdbYH%)qXQ6Uj;4hy4ZJD{IZ9w$<8>zh(-UtuWk$pRR#sv4ezi69N!6(= z8vpXTFjKyOYuo8=W9i>RF>WJ?(K#5~@Aj;7`5@sHFj$o&g+s$sa=f$TW!0JguYtAV z2#p&`$`jrb7{g|a=AFvzBQ=N8EZuU(iKDPfgz^1SrIZAIF=A&MqLHs<*CbS)^X7eT zcsc5><&FZrl`_gW^xW|gR_2jRu~C{o_;DPke|_?$-R0cluf%!+?*7b1OZbg}*TmV> zG}CMMY|?`=6Uce7?qSE!Qh4f4SK?p%*9fCU`BD4DF~vV0+pjh{YF=6ue^)?CHgC&w zX}MKWmv^>JLf#(O664FtJR^}`!=iUjWjY6&M?+laTCW3fI~MH)-CMrvIppVYLgpr` z+LY;QLW_(++R{evK9LODUMrF7CT}dOk4&Nq$*S9#t~7ee^&Z{3L2jJ(g9uP0{2_)< z7ymak!zwU9QcW4CH2){(Tx2Y1N49B0Z=Q zGv=MWTrF=kX>?oOdN0Dl?Q8sMD`08??w+S^NF8)mUucq4+S+#KkM1c*F$XFTl_0{< zpT&;7>&N;xFDsX6*Lk81-AWY4`Z)GS`craS>Zy}}I8Hjfgo3-VH}Cu6ua~jCm#%_e zJDdb0-*~S4y3;QC?hAqe{*uN5#Mf(+&^oD6uM(P<8j9RwC53Hm1dB(Jl2E2ZpvETUE)M`Z$OA z0-0qgzh$v(2jm>M$j&24i-f*vZS#7k7lk+An-L6nKm%?_3G|h>RZI=$?@a0dmb^BXC?3oZQSA?hzLO5X+4HG{M)Mw;A;!hjD|a2UT<$92^kV>qT^g&SgU2)93R7MY zi&UkCDL4vqv+2p@q&4eOWgC3q8t@?1b|5L@JsO~1bY0kzjk-7+Yn-mfJ1P5Y;>op_ zdu>{R>l_8gl9Ob-+)Ow5_S8* zQXf%jFEg$Pq)-mqVJQfzDr}s?ODM23JzxCp$~pS46PUWW&O^^Fm?h@rP@EmOxEHy;T<6bO_pnnliH&qVCedzoGo`&CM z@0GUNaYA7?8q*VCv1&_HS7P+4wR}IJYI`O)y|=H?bR(PsKKt{pI(Yfo;cluMva>^| zQdy~%!%G`z--_&z#2RZAgO)(`ZSI^g7hdAmZ4Bl#WC`|WVuM= zhmg;El5d3E*=aPKoTIO5`0kYy*7zOI7$UR+d_Rlqg-rHN5LZH(z%SQS^_;FantmXV zAMMJRBb?AT>q5T31Nqa{O6~2OqAu!%FqPJicQNo^5h;ZSLg|@@ON`9E~R8S>OC(HGiEN(ToR{?RiSdi!$K^&KIr__^@v)#f>>S13Zwwc zld}A<;70{vj{`d(Q}!Gw0EM=rkHl;*EOR-{z4~=b{r#TQpO5GP$Cfi-1YdS;Q;!1y zLzFJ~f6!Pkqkof+FEdJgF*w+JyZ4<+$^r*_i{5x<1_6jQzJ$svHk) zXYu%T3h}>zOaxx{Arj1-^Beq`$=QtG;A5g}`4e4-(A=k?ulhMr_1$kna)L`T?8i=y zJ|1!Hx;zk7%PeAavykDds+R&xgOhsg{g}W!S_!ZFv@2q>M$>M${Y(zY8gIn-p^`H? z61{vsao}}Zw3fHJdGQbTpFp4hQ{3IXl*U{eVuRRVnbU5MzSInqhN#_B4ihhpCwH32 z6>|vRO>Benv=M<)uk{u07`yB;MHc>TwnCTX$nQY#5tKsryrOqKTL_ authentication;session;identity;efcore;database;auth-framework - logo.png + uauthlogo.png README.md @@ -22,7 +22,7 @@ - + diff --git a/src/authentication/CodeBeam.UltimateAuth.Authentication.EntityFrameworkCore/logo.png b/src/authentication/CodeBeam.UltimateAuth.Authentication.EntityFrameworkCore/logo.png deleted file mode 100644 index aa51a469d9a9fdd0ff8ea6c0711ae28dafc8fb3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3551 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 L597+M377u|47W$d diff --git a/src/authentication/CodeBeam.UltimateAuth.Authentication.EntityFrameworkCore/uauthlogo.png b/src/authentication/CodeBeam.UltimateAuth.Authentication.EntityFrameworkCore/uauthlogo.png new file mode 100644 index 0000000000000000000000000000000000000000..911f2530a1353be33b73719b23571bd6edfb6e96 GIT binary patch literal 4954 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 authentication;session;identity;inmemory;auth-framework - logo.png + uauthlogo.png README.md @@ -22,7 +22,7 @@ - + diff --git a/src/authentication/CodeBeam.UltimateAuth.Authentication.InMemory/logo.png b/src/authentication/CodeBeam.UltimateAuth.Authentication.InMemory/logo.png deleted file mode 100644 index aa51a469d9a9fdd0ff8ea6c0711ae28dafc8fb3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3551 zcmc&Xc{tSF_xCdxBiqO_iq{O4lwu2DSb;A%=F=K0+c{+32sv-c&N$~BD)Y9%>?&y> zYGCoVO#?RugJtI6?Oj4Xikq_##eExOu8#z~sJ`qwn_V95fXleF?#0s0`#q&cntE&{ zK55kr=c5yGn?Vnb{ahD{i-&@Oj@29x{I~| z%Rk>{!TK(Pw$CBSFiRt)Rdhn7l#WkGuW&mn%)<*8=ft-4vFy6s+vBM@AfTpm9=#I# z_ir~V$)f(``KpAok7R%?KMxzKZi%>38gID@G!89Uq{gUNdTfWiu4=4PtnU9R1*`2f ze#5fNLm&R(75n_%#6E7_D$h1J3cfn{={y@=kccUx7S|%P)qJDKt1tuL6 z4{HXr+cB&HWu=Ed-YV*f_?bc23iM<58(B&6pwre^i{SPTD6s}D(vHYh!l()6oQ8rA zH~~l!yb{8eeKeO_o;!&bhCNgj6dA%mXet28I{;`w0M3E`jw*fC=H(Le%r#``^w+V; zcGZt_zu)w#nwipjvS9P5LAz2C#zE{~cK0>fr@i`}UKKU<+|%dlL|@dmS1fib_mjvo zPSUj3W%&}QVtWvMQ0kA=*Y0`fTg2R%8tdidv|UC^&AU8(4lFjbWvbgyOc)2KNpsz6 z#gv?I_toDkJS)8W03Qj`pDw>rT^b(J{A50P>rQuhP<~LXA%cpSbH}4sT;9|UI$)$= z80@c)g@UQ#prNI|G!$T%hj?@2=Cn1?tKO@RNC60YyVW04!T7#IeTXke05$NJuLqac zX!UCBgJCf6I+C4I0{0J&{InU;Z{PjW^jo`q<|Q*!Q&%+H*yuJ0Iq zKYMY#-@)kDa{7^VpP(JTh@|@9gK@j5mTR8}e3vazM3n!<;WPKu-oT>QxYK!=-F_Lj zJ4(8zJ1+6&%kP)&4NBP15C5S}kvA}5&ojFc3tHa!9dnt_Lc$?}^T(l~EN2u13=aWt zRR&P~gu$TVzbO3sV@#s0xxUQC0mJ!j0bvQBkNrV+tI03%k$r{%5*wBk-W}<%css!J z5=UmK%qCxRCCwk7%OXgp#HKC_3jG>v4;oMgV&MG)Jc|=`h-_xdM<=sLci;Y5iV1%8 zFAJr7#X~@mnczomAxvy}!^We$|Ig^yjGauxsh*o5luu#WW;xWtmC=^0zlu%P#*8NC z(kT74pTCZDx>^CnqJ+DB!M8ryM@&04yvb93AJoW;nX30@Y31eaVM*l9X{MdQ*WWqv8l2+O~Bg3TYlN;#aT@JLXy5AopL zF-0i|$>>PdUU4iF@gvK7%b2z&;y>vj1K{3Ns#b^A<0oBp0fi3{8%Ud$&h8k|Uj5!f zh$|c67&6`2xcahv{@C3h8_PsnAgpf|aWiI8C?Gqa53>=Im#-IV^Bq~qwF$Li&vpQ{ zH@+tQGbzY>wZFDy*#Vpm0_rp{4??0LWcq->E_cP6NgVbzlN_xX(;KgGT@9NMiKysV?5pKJn0Hs--I6f{nOg!sU_}alo$0^APDUCoHQyaD0F2$uKGKLal5Am zLbXjt2UGvzk1zDQ{b6dZHN`f0GH%f{X?B5dz?nHFN0FzSF2pXXt0WAj#E!yF1$^Mr zqmf`+fxt!K-vs%OeEI)Dxd^IWu;F0r1adV~76d;%0&Dh85{@hfgB>5^&A#o!T^=l5J9n@xYn<1|^yrsml!Ne-3ol7+ z9HY;8^!4KtU)91t_TIk`UOk(AqP}vcrY7s#a3{OJVJ5kyrNO|-t)ChI=$J*gWw@V5oaJ~r7Or_sR7M(0zd56w)T$aW;@`8r<{FZCe{Cqw zlze}Y7Z4vj7uFN*%AEG5eoYlk&0QO_SRXQ?v5n6Dy3~De1#&LU(BU!myf2`vG)#S4 zm@pa0E^uh^tC-W=k)|yqPrf&*SscDu+~p|M9Uha5AsHRTO*F1-n{+_WT$^bex!pL# z{|$0tv^0HGK{2J{5sibs-1Pj_{*b>w&B*Re1c6m&ts^2Lum$YF=Tf`g%CvYMU3d7Ti(wX7kU!8wqJW`T*VFqzj5ru#pS8SY?%U zr7$K%TYXPIVAO}edyYRa@gOI?%|F#xLBGmWAyyoBhyqgu-BnEPDK;3y5zizq7@$9Azlg z=i1bA)HOV4HH#2NKnH{Ne~kKN^3q>|5Z6fWAC-C-=_IuIU{~XLecm4R%_(Aq?~i$D48BbU zS}`RG%kSSHF5OIz0ti)VZP*@@y@O81&O{t&BlAc29(@iDy zXY&OHs-Sj^G~9D^r%BwdhCr42geey2bBK18@h+j)SNU{W>>Ggne8Fd`-Od+8S)mBK zxoL%X*lH1zm^{S$4G;V``pZD0+#98}lPbGl`TMLf@^TarVN3aVYNQ;+R@m~lvf9H+ z=IH~;#EEDr-kA7=s_(5n#{9?EI<@o=nO-Y?Hx7Joi>H#rt`=Q@J{fBEckG!;b4Gbn z|MaiDhCR?Wj^2s~&yV|h->4+NR+~A}i@K&vV?(CU!358^JJ|XPD`Cs)h*+0Gfl~9) zL3Me9;V)||0S8M0yl89}Qz#WCo0!!soG0@|j37r-imY0U+5X%Z&!7nW^b4TCoHps8}MDB1EpzTAwfZPCL)JtjlrHa6C}uXN4L(9Trcu;xP0{ibHTV zPmPOK!GiE8`PBok*BYE??%U+xa1YBdt4rJ4{r!;o037zV5paVTj<=7=lpz zVUTnIbbWimND?tS#8==Hq!|cbT7G}Um2{{shjqT4C<_rjEssSHtw@g9uX_rO#0FvE zjLUOfNNll%Oe$1_DJyI4snb+7g^G&K+l0QHe$0tp*F(pVlQH1Psfb&uq$vj^#MtZg z{>tsHYz-L3pI$bAOs{(riqj^XJwFF>z$7iLs%F1QPboPY8gj9ct^Ml?zwLBOTjrZPFZU L597+M377u|47W$d diff --git a/src/authentication/CodeBeam.UltimateAuth.Authentication.InMemory/uauthlogo.png b/src/authentication/CodeBeam.UltimateAuth.Authentication.InMemory/uauthlogo.png new file mode 100644 index 0000000000000000000000000000000000000000..911f2530a1353be33b73719b23571bd6edfb6e96 GIT binary patch literal 4954 zcmcgwWmFVEw8j;Xg(a76mW8EDLILSmU};dg1$nfzN~|mb0>UDKq##I2hf7FENJvPt zbgXnOpwe&s`ObOq@BNs$_sq;W_s-n8-*;!?j106X$yv#Xh=?e4;2I_bdHAo9krH|( z^Q{zu0Q$l${fUSu=>D}^V+-MzL_}baj)tmPNcLWiz9+pl>nr8aU3V2q?ymO^#*<)B?F#;?x@e z)9+HyW~+1-H>^+21!UDMn43PhYim1ss+97s&v|z{_gPU{`}uacB^V5LiuYr&7KZ%Y zj%}CMbjfWXr5Ov)jqUTIWr=IAd|3Ewaw=@teA$mBs_;Qg{X)|fkJ@|zX@;gt31kx8 zYLy<}u_i_8{>8*f4Z?k#@!Bi~8>WC2I>aQXVn;Mw{N}<1d2o*O;bw);ifkWEFe2Az z{|LX0MGD+o&H69uv>uZ8!NO7Z9$$7_0@voyIQ*Np#w3i?p>#2;s@vp5pnZ)GbMR*_p^5kW;p(5gU=xwOPizDcSd0L(2jk2RMw)n zi3{|3K+$8*Qew`hq7UdyonxPW3|d4^CDY6JOaSU`Fbxey=n40>Ptk2nB%M$B zlA)rlgPWH9`~*YGl6Q!@AFrIxW{N0V^Xts1WV%~%BX+E5{^_q0&lfAeOrF$?rdlgH zNZaSXZQCP?F9QzJ@PYG+#Pq)dd}^^`bfjdNoLsD_YYnOueN|$%w29qm87~+sN4ibK z80Sdk^kL#tn}_|{p*nWlV+pqaSD-6>l5;^Vk3mE;YhuSy^FbKXv)f8%cG%yUcvEV* zV>~)MUFtEsU-g5{O~1k8Z#0nc_VA0Sy{u~Dop1eQiq2uB0ZTWQ5sG=Ig?5ZX*Y?H$P_!;}T!_9t` zF6Z^E!>ueY!b)3=T(e;n)h`)v%;+ZrD=L@3;h@LkN{oZ%hiB;+l($x_J}qO$^h7@h zMI)EjfeHbU@OL{4Nbqo~BY{WE zB!~!}Wu|sg6LOV*3@f;CN~aP+IqkqG#AlGEn=d>4Bn3WD0ADDF0`|TK<`uD!gHtZ^ zJ4k35bm!XdZXZ5Y2n0;m9ZWF&jALzP4<3`J$8a0rj2_8<0WA~jz!CT;0F%l==}6T5 zab_+Pj9m&f{ftz}1p}706tf5@Gbg@9(g=F_kcVzNaD$Shkt{8K;Cj9sFw&J)PMDiU zil%(peKK9vFrW*4|3;0>y1raAF$~S|P2C zYnz?(am`z9|eu;IG) z^#{hUjTUHGO!}|`Sp<+s8o0!aGD?97zLXG!o_tMeNPLO7{WZ*()j3x%;R(g{8wxL; zEno6IW=rx(obAN$r^e&}fokU;ADqK0z0*E;krI3u`=88z?|VuUwAXy*9y65uNpyN2 zKVJaJ>tNinNX{KHqprJ<3BJ;u;! zd$Py^REEuG~?uGko+0-e@##uwc3eVNVqyuk96B5!|aM1 z7nxzf2f**&`(1iO!XY0ab#8*B1paUjVtdoU$5Y-~$ZoWpq7vGL`j^u5|Mz|R|5(yX zf#gcMT2T=u3+>(Px-&cvbR)LAUKJjg{>P(}jWi$E8jF-GEt|)-zxrz}6S!2WN*;FF zfIStSnp2N`?eWLEsI4}nlVa*1!_@T2Wbt%E%a&Y1GKBddNTAu$#SX)FSQ~&mSiFfI zf3ixv8AD(_LoeMw+ksPAo2eZO{;Eq(5oLCZ25h6ClO-rXkUP5YF}6Q}^onZA~%OP+1L6dPRLDkq9XoS^d~S z0B6C1pWi*A*rRoL@|N;%J|VB0L)9Pg>^(Jb5P|J&{SrXTe|r>< zj}v&Yd8tz@B;m* zUs)zAIdbYH%)qXQ6Uj;4hy4ZJD{IZ9w$<8>zh(-UtuWk$pRR#sv4ezi69N!6(= z8vpXTFjKyOYuo8=W9i>RF>WJ?(K#5~@Aj;7`5@sHFj$o&g+s$sa=f$TW!0JguYtAV z2#p&`$`jrb7{g|a=AFvzBQ=N8EZuU(iKDPfgz^1SrIZAIF=A&MqLHs<*CbS)^X7eT zcsc5><&FZrl`_gW^xW|gR_2jRu~C{o_;DPke|_?$-R0cluf%!+?*7b1OZbg}*TmV> zG}CMMY|?`=6Uce7?qSE!Qh4f4SK?p%*9fCU`BD4DF~vV0+pjh{YF=6ue^)?CHgC&w zX}MKWmv^>JLf#(O664FtJR^}`!=iUjWjY6&M?+laTCW3fI~MH)-CMrvIppVYLgpr` z+LY;QLW_(++R{evK9LODUMrF7CT}dOk4&Nq$*S9#t~7ee^&Z{3L2jJ(g9uP0{2_)< z7ymak!zwU9QcW4CH2){(Tx2Y1N49B0Z=Q zGv=MWTrF=kX>?oOdN0Dl?Q8sMD`08??w+S^NF8)mUucq4+S+#KkM1c*F$XFTl_0{< zpT&;7>&N;xFDsX6*Lk81-AWY4`Z)GS`craS>Zy}}I8Hjfgo3-VH}Cu6ua~jCm#%_e zJDdb0-*~S4y3;QC?hAqe{*uN5#Mf(+&^oD6uM(P<8j9RwC53Hm1dB(Jl2E2ZpvETUE)M`Z$OA z0-0qgzh$v(2jm>M$j&24i-f*vZS#7k7lk+An-L6nKm%?_3G|h>RZI=$?@a0dmb^BXC?3oZQSA?hzLO5X+4HG{M)Mw;A;!hjD|a2UT<$92^kV>qT^g&SgU2)93R7MY zi&UkCDL4vqv+2p@q&4eOWgC3q8t@?1b|5L@JsO~1bY0kzjk-7+Yn-mfJ1P5Y;>op_ zdu>{R>l_8gl9Ob-+)Ow5_S8* zQXf%jFEg$Pq)-mqVJQfzDr}s?ODM23JzxCp$~pS46PUWW&O^^Fm?h@rP@EmOxEHy;T<6bO_pnnliH&qVCedzoGo`&CM z@0GUNaYA7?8q*VCv1&_HS7P+4wR}IJYI`O)y|=H?bR(PsKKt{pI(Yfo;cluMva>^| zQdy~%!%G`z--_&z#2RZAgO)(`ZSI^g7hdAmZ4Bl#WC`|WVuM= zhmg;El5d3E*=aPKoTIO5`0kYy*7zOI7$UR+d_Rlqg-rHN5LZH(z%SQS^_;FantmXV zAMMJRBb?AT>q5T31Nqa{O6~2OqAu!%FqPJicQNo^5h;ZSLg|@@ON`9E~R8S>OC(HGiEN(ToR{?RiSdi!$K^&KIr__^@v)#f>>S13Zwwc zld}A<;70{vj{`d(Q}!Gw0EM=rkHl;*EOR-{z4~=b{r#TQpO5GP$Cfi-1YdS;Q;!1y zLzFJ~f6!Pkqkof+FEdJgF*w+JyZ4<+$^r*_i{5x<1_6jQzJ$svHk) zXYu%T3h}>zOaxx{Arj1-^Beq`$=QtG;A5g}`4e4-(A=k?ulhMr_1$kna)L`T?8i=y zJ|1!Hx;zk7%PeAavykDds+R&xgOhsg{g}W!S_!ZFv@2q>M$>M${Y(zY8gIn-p^`H? z61{vsao}}Zw3fHJdGQbTpFp4hQ{3IXl*U{eVuRRVnbU5MzSInqhN#_B4ihhpCwH32 z6>|vRO>Benv=M<)uk{u07`yB;MHc>TwnCTX$nQY#5tKsryrOqKTL_ authentication;authorization;roles;permissions;contracts;shared;auth-framework - logo.png + uauthlogo.png README.md @@ -22,7 +22,7 @@ - + diff --git a/src/authorization/CodeBeam.UltimateAuth.Authorization.Contracts/logo.png b/src/authorization/CodeBeam.UltimateAuth.Authorization.Contracts/logo.png deleted file mode 100644 index aa51a469d9a9fdd0ff8ea6c0711ae28dafc8fb3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3551 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 L597+M377u|47W$d diff --git a/src/authorization/CodeBeam.UltimateAuth.Authorization.Contracts/uauthlogo.png b/src/authorization/CodeBeam.UltimateAuth.Authorization.Contracts/uauthlogo.png new file mode 100644 index 0000000000000000000000000000000000000000..911f2530a1353be33b73719b23571bd6edfb6e96 GIT binary patch literal 4954 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 authentication;authorization;roles;claims;efcore;database;auth-framework - logo.png + uauthlogo.png README.md @@ -23,7 +23,7 @@ - + diff --git a/src/authorization/CodeBeam.UltimateAuth.Authorization.EntityFrameworkCore/logo.png b/src/authorization/CodeBeam.UltimateAuth.Authorization.EntityFrameworkCore/logo.png deleted file mode 100644 index aa51a469d9a9fdd0ff8ea6c0711ae28dafc8fb3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3551 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 L597+M377u|47W$d diff --git a/src/authorization/CodeBeam.UltimateAuth.Authorization.EntityFrameworkCore/uauthlogo.png b/src/authorization/CodeBeam.UltimateAuth.Authorization.EntityFrameworkCore/uauthlogo.png new file mode 100644 index 0000000000000000000000000000000000000000..911f2530a1353be33b73719b23571bd6edfb6e96 GIT binary patch literal 4954 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 authentication;authorization;roles;claims;inmemory;auth-framework - logo.png + uauthlogo.png README.md @@ -24,7 +24,7 @@ - + diff --git a/src/authorization/CodeBeam.UltimateAuth.Authorization.InMemory/logo.png b/src/authorization/CodeBeam.UltimateAuth.Authorization.InMemory/logo.png deleted file mode 100644 index aa51a469d9a9fdd0ff8ea6c0711ae28dafc8fb3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3551 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 L597+M377u|47W$d diff --git a/src/authorization/CodeBeam.UltimateAuth.Authorization.InMemory/uauthlogo.png b/src/authorization/CodeBeam.UltimateAuth.Authorization.InMemory/uauthlogo.png new file mode 100644 index 0000000000000000000000000000000000000000..911f2530a1353be33b73719b23571bd6edfb6e96 GIT binary patch literal 4954 zcmcgwWmFVEw8j;Xg(a76mW8EDLILSmU};dg1$nfzN~|mb0>UDKq##I2hf7FENJvPt zbgXnOpwe&s`ObOq@BNs$_sq;W_s-n8-*;!?j106X$yv#Xh=?e4;2I_bdHAo9krH|( z^Q{zu0Q$l${fUSu=>D}^V+-MzL_}baj)tmPNcLWiz9+pl>nr8aU3V2q?ymO^#*<)B?F#;?x@e z)9+HyW~+1-H>^+21!UDMn43PhYim1ss+97s&v|z{_gPU{`}uacB^V5LiuYr&7KZ%Y zj%}CMbjfWXr5Ov)jqUTIWr=IAd|3Ewaw=@teA$mBs_;Qg{X)|fkJ@|zX@;gt31kx8 zYLy<}u_i_8{>8*f4Z?k#@!Bi~8>WC2I>aQXVn;Mw{N}<1d2o*O;bw);ifkWEFe2Az z{|LX0MGD+o&H69uv>uZ8!NO7Z9$$7_0@voyIQ*Np#w3i?p>#2;s@vp5pnZ)GbMR*_p^5kW;p(5gU=xwOPizDcSd0L(2jk2RMw)n zi3{|3K+$8*Qew`hq7UdyonxPW3|d4^CDY6JOaSU`Fbxey=n40>Ptk2nB%M$B zlA)rlgPWH9`~*YGl6Q!@AFrIxW{N0V^Xts1WV%~%BX+E5{^_q0&lfAeOrF$?rdlgH zNZaSXZQCP?F9QzJ@PYG+#Pq)dd}^^`bfjdNoLsD_YYnOueN|$%w29qm87~+sN4ibK z80Sdk^kL#tn}_|{p*nWlV+pqaSD-6>l5;^Vk3mE;YhuSy^FbKXv)f8%cG%yUcvEV* zV>~)MUFtEsU-g5{O~1k8Z#0nc_VA0Sy{u~Dop1eQiq2uB0ZTWQ5sG=Ig?5ZX*Y?H$P_!;}T!_9t` zF6Z^E!>ueY!b)3=T(e;n)h`)v%;+ZrD=L@3;h@LkN{oZ%hiB;+l($x_J}qO$^h7@h zMI)EjfeHbU@OL{4Nbqo~BY{WE zB!~!}Wu|sg6LOV*3@f;CN~aP+IqkqG#AlGEn=d>4Bn3WD0ADDF0`|TK<`uD!gHtZ^ zJ4k35bm!XdZXZ5Y2n0;m9ZWF&jALzP4<3`J$8a0rj2_8<0WA~jz!CT;0F%l==}6T5 zab_+Pj9m&f{ftz}1p}706tf5@Gbg@9(g=F_kcVzNaD$Shkt{8K;Cj9sFw&J)PMDiU zil%(peKK9vFrW*4|3;0>y1raAF$~S|P2C zYnz?(am`z9|eu;IG) z^#{hUjTUHGO!}|`Sp<+s8o0!aGD?97zLXG!o_tMeNPLO7{WZ*()j3x%;R(g{8wxL; zEno6IW=rx(obAN$r^e&}fokU;ADqK0z0*E;krI3u`=88z?|VuUwAXy*9y65uNpyN2 zKVJaJ>tNinNX{KHqprJ<3BJ;u;! zd$Py^REEuG~?uGko+0-e@##uwc3eVNVqyuk96B5!|aM1 z7nxzf2f**&`(1iO!XY0ab#8*B1paUjVtdoU$5Y-~$ZoWpq7vGL`j^u5|Mz|R|5(yX zf#gcMT2T=u3+>(Px-&cvbR)LAUKJjg{>P(}jWi$E8jF-GEt|)-zxrz}6S!2WN*;FF zfIStSnp2N`?eWLEsI4}nlVa*1!_@T2Wbt%E%a&Y1GKBddNTAu$#SX)FSQ~&mSiFfI zf3ixv8AD(_LoeMw+ksPAo2eZO{;Eq(5oLCZ25h6ClO-rXkUP5YF}6Q}^onZA~%OP+1L6dPRLDkq9XoS^d~S z0B6C1pWi*A*rRoL@|N;%J|VB0L)9Pg>^(Jb5P|J&{SrXTe|r>< zj}v&Yd8tz@B;m* zUs)zAIdbYH%)qXQ6Uj;4hy4ZJD{IZ9w$<8>zh(-UtuWk$pRR#sv4ezi69N!6(= z8vpXTFjKyOYuo8=W9i>RF>WJ?(K#5~@Aj;7`5@sHFj$o&g+s$sa=f$TW!0JguYtAV z2#p&`$`jrb7{g|a=AFvzBQ=N8EZuU(iKDPfgz^1SrIZAIF=A&MqLHs<*CbS)^X7eT zcsc5><&FZrl`_gW^xW|gR_2jRu~C{o_;DPke|_?$-R0cluf%!+?*7b1OZbg}*TmV> zG}CMMY|?`=6Uce7?qSE!Qh4f4SK?p%*9fCU`BD4DF~vV0+pjh{YF=6ue^)?CHgC&w zX}MKWmv^>JLf#(O664FtJR^}`!=iUjWjY6&M?+laTCW3fI~MH)-CMrvIppVYLgpr` z+LY;QLW_(++R{evK9LODUMrF7CT}dOk4&Nq$*S9#t~7ee^&Z{3L2jJ(g9uP0{2_)< z7ymak!zwU9QcW4CH2){(Tx2Y1N49B0Z=Q zGv=MWTrF=kX>?oOdN0Dl?Q8sMD`08??w+S^NF8)mUucq4+S+#KkM1c*F$XFTl_0{< zpT&;7>&N;xFDsX6*Lk81-AWY4`Z)GS`craS>Zy}}I8Hjfgo3-VH}Cu6ua~jCm#%_e zJDdb0-*~S4y3;QC?hAqe{*uN5#Mf(+&^oD6uM(P<8j9RwC53Hm1dB(Jl2E2ZpvETUE)M`Z$OA z0-0qgzh$v(2jm>M$j&24i-f*vZS#7k7lk+An-L6nKm%?_3G|h>RZI=$?@a0dmb^BXC?3oZQSA?hzLO5X+4HG{M)Mw;A;!hjD|a2UT<$92^kV>qT^g&SgU2)93R7MY zi&UkCDL4vqv+2p@q&4eOWgC3q8t@?1b|5L@JsO~1bY0kzjk-7+Yn-mfJ1P5Y;>op_ zdu>{R>l_8gl9Ob-+)Ow5_S8* zQXf%jFEg$Pq)-mqVJQfzDr}s?ODM23JzxCp$~pS46PUWW&O^^Fm?h@rP@EmOxEHy;T<6bO_pnnliH&qVCedzoGo`&CM z@0GUNaYA7?8q*VCv1&_HS7P+4wR}IJYI`O)y|=H?bR(PsKKt{pI(Yfo;cluMva>^| zQdy~%!%G`z--_&z#2RZAgO)(`ZSI^g7hdAmZ4Bl#WC`|WVuM= zhmg;El5d3E*=aPKoTIO5`0kYy*7zOI7$UR+d_Rlqg-rHN5LZH(z%SQS^_;FantmXV zAMMJRBb?AT>q5T31Nqa{O6~2OqAu!%FqPJicQNo^5h;ZSLg|@@ON`9E~R8S>OC(HGiEN(ToR{?RiSdi!$K^&KIr__^@v)#f>>S13Zwwc zld}A<;70{vj{`d(Q}!Gw0EM=rkHl;*EOR-{z4~=b{r#TQpO5GP$Cfi-1YdS;Q;!1y zLzFJ~f6!Pkqkof+FEdJgF*w+JyZ4<+$^r*_i{5x<1_6jQzJ$svHk) zXYu%T3h}>zOaxx{Arj1-^Beq`$=QtG;A5g}`4e4-(A=k?ulhMr_1$kna)L`T?8i=y zJ|1!Hx;zk7%PeAavykDds+R&xgOhsg{g}W!S_!ZFv@2q>M$>M${Y(zY8gIn-p^`H? z61{vsao}}Zw3fHJdGQbTpFp4hQ{3IXl*U{eVuRRVnbU5MzSInqhN#_B4ihhpCwH32 z6>|vRO>Benv=M<)uk{u07`yB;MHc>TwnCTX$nQY#5tKsryrOqKTL_ authorization;roles;claims;rbac;policy;security;auth-framework - logo.png + uauthlogo.png README.md @@ -24,7 +24,7 @@ - + diff --git a/src/authorization/CodeBeam.UltimateAuth.Authorization.Reference/logo.png b/src/authorization/CodeBeam.UltimateAuth.Authorization.Reference/logo.png deleted file mode 100644 index aa51a469d9a9fdd0ff8ea6c0711ae28dafc8fb3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3551 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 L597+M377u|47W$d diff --git a/src/authorization/CodeBeam.UltimateAuth.Authorization.Reference/uauthlogo.png b/src/authorization/CodeBeam.UltimateAuth.Authorization.Reference/uauthlogo.png new file mode 100644 index 0000000000000000000000000000000000000000..911f2530a1353be33b73719b23571bd6edfb6e96 GIT binary patch literal 4954 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 authentication;authorization;roles;permissions;security;module;auth-framework - logo.png + uauthlogo.png README.md @@ -24,7 +24,7 @@ - + diff --git a/src/authorization/CodeBeam.UltimateAuth.Authorization/logo.png b/src/authorization/CodeBeam.UltimateAuth.Authorization/logo.png deleted file mode 100644 index aa51a469d9a9fdd0ff8ea6c0711ae28dafc8fb3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3551 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 L597+M377u|47W$d diff --git a/src/authorization/CodeBeam.UltimateAuth.Authorization/uauthlogo.png b/src/authorization/CodeBeam.UltimateAuth.Authorization/uauthlogo.png new file mode 100644 index 0000000000000000000000000000000000000000..911f2530a1353be33b73719b23571bd6edfb6e96 GIT binary patch literal 4954 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 authentication;authorization;identity;blazor;wasm;aspnetcore;client;auth;oauth;pkce;jwt;auth-framework - logo.png + uauthlogo.png README.md @@ -57,7 +57,7 @@ - + diff --git a/src/client/CodeBeam.UltimateAuth.Client.Blazor/logo.png b/src/client/CodeBeam.UltimateAuth.Client.Blazor/logo.png deleted file mode 100644 index aa51a469d9a9fdd0ff8ea6c0711ae28dafc8fb3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3551 zcmc&Xc{tSF_xCdxBiqO_iq{O4lwu2DSb;A%=F=K0+c{+32sv-c&N$~BD)Y9%>?&y> zYGCoVO#?RugJtI6?Oj4Xikq_##eExOu8#z~sJ`qwn_V95fXleF?#0s0`#q&cntE&{ zK55kr=c5yGn?Vnb{ahD{i-&@Oj@29x{I~| z%Rk>{!TK(Pw$CBSFiRt)Rdhn7l#WkGuW&mn%)<*8=ft-4vFy6s+vBM@AfTpm9=#I# z_ir~V$)f(``KpAok7R%?KMxzKZi%>38gID@G!89Uq{gUNdTfWiu4=4PtnU9R1*`2f ze#5fNLm&R(75n_%#6E7_D$h1J3cfn{={y@=kccUx7S|%P)qJDKt1tuL6 z4{HXr+cB&HWu=Ed-YV*f_?bc23iM<58(B&6pwre^i{SPTD6s}D(vHYh!l()6oQ8rA zH~~l!yb{8eeKeO_o;!&bhCNgj6dA%mXet28I{;`w0M3E`jw*fC=H(Le%r#``^w+V; zcGZt_zu)w#nwipjvS9P5LAz2C#zE{~cK0>fr@i`}UKKU<+|%dlL|@dmS1fib_mjvo zPSUj3W%&}QVtWvMQ0kA=*Y0`fTg2R%8tdidv|UC^&AU8(4lFjbWvbgyOc)2KNpsz6 z#gv?I_toDkJS)8W03Qj`pDw>rT^b(J{A50P>rQuhP<~LXA%cpSbH}4sT;9|UI$)$= z80@c)g@UQ#prNI|G!$T%hj?@2=Cn1?tKO@RNC60YyVW04!T7#IeTXke05$NJuLqac zX!UCBgJCf6I+C4I0{0J&{InU;Z{PjW^jo`q<|Q*!Q&%+H*yuJ0Iq zKYMY#-@)kDa{7^VpP(JTh@|@9gK@j5mTR8}e3vazM3n!<;WPKu-oT>QxYK!=-F_Lj zJ4(8zJ1+6&%kP)&4NBP15C5S}kvA}5&ojFc3tHa!9dnt_Lc$?}^T(l~EN2u13=aWt zRR&P~gu$TVzbO3sV@#s0xxUQC0mJ!j0bvQBkNrV+tI03%k$r{%5*wBk-W}<%css!J z5=UmK%qCxRCCwk7%OXgp#HKC_3jG>v4;oMgV&MG)Jc|=`h-_xdM<=sLci;Y5iV1%8 zFAJr7#X~@mnczomAxvy}!^We$|Ig^yjGauxsh*o5luu#WW;xWtmC=^0zlu%P#*8NC z(kT74pTCZDx>^CnqJ+DB!M8ryM@&04yvb93AJoW;nX30@Y31eaVM*l9X{MdQ*WWqv8l2+O~Bg3TYlN;#aT@JLXy5AopL zF-0i|$>>PdUU4iF@gvK7%b2z&;y>vj1K{3Ns#b^A<0oBp0fi3{8%Ud$&h8k|Uj5!f zh$|c67&6`2xcahv{@C3h8_PsnAgpf|aWiI8C?Gqa53>=Im#-IV^Bq~qwF$Li&vpQ{ zH@+tQGbzY>wZFDy*#Vpm0_rp{4??0LWcq->E_cP6NgVbzlN_xX(;KgGT@9NMiKysV?5pKJn0Hs--I6f{nOg!sU_}alo$0^APDUCoHQyaD0F2$uKGKLal5Am zLbXjt2UGvzk1zDQ{b6dZHN`f0GH%f{X?B5dz?nHFN0FzSF2pXXt0WAj#E!yF1$^Mr zqmf`+fxt!K-vs%OeEI)Dxd^IWu;F0r1adV~76d;%0&Dh85{@hfgB>5^&A#o!T^=l5J9n@xYn<1|^yrsml!Ne-3ol7+ z9HY;8^!4KtU)91t_TIk`UOk(AqP}vcrY7s#a3{OJVJ5kyrNO|-t)ChI=$J*gWw@V5oaJ~r7Or_sR7M(0zd56w)T$aW;@`8r<{FZCe{Cqw zlze}Y7Z4vj7uFN*%AEG5eoYlk&0QO_SRXQ?v5n6Dy3~De1#&LU(BU!myf2`vG)#S4 zm@pa0E^uh^tC-W=k)|yqPrf&*SscDu+~p|M9Uha5AsHRTO*F1-n{+_WT$^bex!pL# z{|$0tv^0HGK{2J{5sibs-1Pj_{*b>w&B*Re1c6m&ts^2Lum$YF=Tf`g%CvYMU3d7Ti(wX7kU!8wqJW`T*VFqzj5ru#pS8SY?%U zr7$K%TYXPIVAO}edyYRa@gOI?%|F#xLBGmWAyyoBhyqgu-BnEPDK;3y5zizq7@$9Azlg z=i1bA)HOV4HH#2NKnH{Ne~kKN^3q>|5Z6fWAC-C-=_IuIU{~XLecm4R%_(Aq?~i$D48BbU zS}`RG%kSSHF5OIz0ti)VZP*@@y@O81&O{t&BlAc29(@iDy zXY&OHs-Sj^G~9D^r%BwdhCr42geey2bBK18@h+j)SNU{W>>Ggne8Fd`-Od+8S)mBK zxoL%X*lH1zm^{S$4G;V``pZD0+#98}lPbGl`TMLf@^TarVN3aVYNQ;+R@m~lvf9H+ z=IH~;#EEDr-kA7=s_(5n#{9?EI<@o=nO-Y?Hx7Joi>H#rt`=Q@J{fBEckG!;b4Gbn z|MaiDhCR?Wj^2s~&yV|h->4+NR+~A}i@K&vV?(CU!358^JJ|XPD`Cs)h*+0Gfl~9) zL3Me9;V)||0S8M0yl89}Qz#WCo0!!soG0@|j37r-imY0U+5X%Z&!7nW^b4TCoHps8}MDB1EpzTAwfZPCL)JtjlrHa6C}uXN4L(9Trcu;xP0{ibHTV zPmPOK!GiE8`PBok*BYE??%U+xa1YBdt4rJ4{r!;o037zV5paVTj<=7=lpz zVUTnIbbWimND?tS#8==Hq!|cbT7G}Um2{{shjqT4C<_rjEssSHtw@g9uX_rO#0FvE zjLUOfNNll%Oe$1_DJyI4snb+7g^G&K+l0QHe$0tp*F(pVlQH1Psfb&uq$vj^#MtZg z{>tsHYz-L3pI$bAOs{(riqj^XJwFF>z$7iLs%F1QPboPY8gj9ct^Ml?zwLBOTjrZPFZU L597+M377u|47W$d diff --git a/src/client/CodeBeam.UltimateAuth.Client.Blazor/uauthlogo.png b/src/client/CodeBeam.UltimateAuth.Client.Blazor/uauthlogo.png new file mode 100644 index 0000000000000000000000000000000000000000..911f2530a1353be33b73719b23571bd6edfb6e96 GIT binary patch literal 4954 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 client engine for UltimateAuth. Provides platform-agnostic authentication features. This package does NOT include transport, storage or UI integration. For complete experience, use a platform adapter such as CodeBeam.UltimateAuth.Client.Blazor authentication;authorization;identity;aspnetcore;auth;oauth;pkce;jwt;auth-framework - logo.png + uauthlogo.png README.md @@ -20,7 +20,7 @@ - + diff --git a/src/client/CodeBeam.UltimateAuth.Client/logo.png b/src/client/CodeBeam.UltimateAuth.Client/logo.png deleted file mode 100644 index aa51a469d9a9fdd0ff8ea6c0711ae28dafc8fb3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3551 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 L597+M377u|47W$d diff --git a/src/client/CodeBeam.UltimateAuth.Client/uauthlogo.png b/src/client/CodeBeam.UltimateAuth.Client/uauthlogo.png new file mode 100644 index 0000000000000000000000000000000000000000..911f2530a1353be33b73719b23571bd6edfb6e96 GIT binary patch literal 4954 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 authentication;credentials;identity;contracts;shared;dto;auth-framework - logo.png + uauthlogo.png README.md @@ -22,7 +22,7 @@ - + diff --git a/src/credentials/CodeBeam.UltimateAuth.Credentials.Contracts/logo.png b/src/credentials/CodeBeam.UltimateAuth.Credentials.Contracts/logo.png deleted file mode 100644 index aa51a469d9a9fdd0ff8ea6c0711ae28dafc8fb3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3551 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 L597+M377u|47W$d diff --git a/src/credentials/CodeBeam.UltimateAuth.Credentials.Contracts/uauthlogo.png b/src/credentials/CodeBeam.UltimateAuth.Credentials.Contracts/uauthlogo.png new file mode 100644 index 0000000000000000000000000000000000000000..911f2530a1353be33b73719b23571bd6edfb6e96 GIT binary patch literal 4954 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 authentication;credentials;efcore;password;database;auth-framework - logo.png + uauthlogo.png README.md @@ -24,7 +24,7 @@ - + diff --git a/src/credentials/CodeBeam.UltimateAuth.Credentials.EntityFrameworkCore/logo.png b/src/credentials/CodeBeam.UltimateAuth.Credentials.EntityFrameworkCore/logo.png deleted file mode 100644 index aa51a469d9a9fdd0ff8ea6c0711ae28dafc8fb3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3551 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 L597+M377u|47W$d diff --git a/src/credentials/CodeBeam.UltimateAuth.Credentials.EntityFrameworkCore/uauthlogo.png b/src/credentials/CodeBeam.UltimateAuth.Credentials.EntityFrameworkCore/uauthlogo.png new file mode 100644 index 0000000000000000000000000000000000000000..911f2530a1353be33b73719b23571bd6edfb6e96 GIT binary patch literal 4954 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 authentication;credentials;inmemory;password;auth-framework - logo.png + uauthlogo.png README.md @@ -25,7 +25,7 @@ - + diff --git a/src/credentials/CodeBeam.UltimateAuth.Credentials.InMemory/logo.png b/src/credentials/CodeBeam.UltimateAuth.Credentials.InMemory/logo.png deleted file mode 100644 index aa51a469d9a9fdd0ff8ea6c0711ae28dafc8fb3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3551 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 L597+M377u|47W$d diff --git a/src/credentials/CodeBeam.UltimateAuth.Credentials.InMemory/uauthlogo.png b/src/credentials/CodeBeam.UltimateAuth.Credentials.InMemory/uauthlogo.png new file mode 100644 index 0000000000000000000000000000000000000000..911f2530a1353be33b73719b23571bd6edfb6e96 GIT binary patch literal 4954 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 authentication;credentials;password;security;reference;auth-framework - logo.png + uauthlogo.png README.md @@ -23,7 +23,7 @@ - + diff --git a/src/credentials/CodeBeam.UltimateAuth.Credentials.Reference/logo.png b/src/credentials/CodeBeam.UltimateAuth.Credentials.Reference/logo.png deleted file mode 100644 index aa51a469d9a9fdd0ff8ea6c0711ae28dafc8fb3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3551 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 L597+M377u|47W$d diff --git a/src/credentials/CodeBeam.UltimateAuth.Credentials.Reference/uauthlogo.png b/src/credentials/CodeBeam.UltimateAuth.Credentials.Reference/uauthlogo.png new file mode 100644 index 0000000000000000000000000000000000000000..911f2530a1353be33b73719b23571bd6edfb6e96 GIT binary patch literal 4954 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 authentication;credentials;identity;security;module;auth-framework - logo.png + uauthlogo.png README.md @@ -24,7 +24,7 @@ - + diff --git a/src/credentials/CodeBeam.UltimateAuth.Credentials/logo.png b/src/credentials/CodeBeam.UltimateAuth.Credentials/logo.png deleted file mode 100644 index aa51a469d9a9fdd0ff8ea6c0711ae28dafc8fb3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3551 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 L597+M377u|47W$d diff --git a/src/credentials/CodeBeam.UltimateAuth.Credentials/uauthlogo.png b/src/credentials/CodeBeam.UltimateAuth.Credentials/uauthlogo.png new file mode 100644 index 0000000000000000000000000000000000000000..911f2530a1353be33b73719b23571bd6edfb6e96 GIT binary patch literal 4954 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 authentication;efcore;infrastructure;orm;auth-framework - logo.png + uauthlogo.png README.md @@ -35,7 +35,7 @@ - + diff --git a/src/persistence/CodeBeam.UltimateAuth.EntityFrameworkCore/logo.png b/src/persistence/CodeBeam.UltimateAuth.EntityFrameworkCore/logo.png deleted file mode 100644 index aa51a469d9a9fdd0ff8ea6c0711ae28dafc8fb3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3551 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 L597+M377u|47W$d diff --git a/src/persistence/CodeBeam.UltimateAuth.EntityFrameworkCore/uauthlogo.png b/src/persistence/CodeBeam.UltimateAuth.EntityFrameworkCore/uauthlogo.png new file mode 100644 index 0000000000000000000000000000000000000000..911f2530a1353be33b73719b23571bd6edfb6e96 GIT binary patch literal 4954 zcmcgwWmFVEw8j;Xg(a76mW8EDLILSmU};dg1$nfzN~|mb0>UDKq##I2hf7FENJvPt zbgXnOpwe&s`ObOq@BNs$_sq;W_s-n8-*;!?j106X$yv#Xh=?e4;2I_bdHAo9krH|( z^Q{zu0Q$l${fUSu=>D}^V+-MzL_}baj)tmPNcLWiz9+pl>nr8aU3V2q?ymO^#*<)B?F#;?x@e z)9+HyW~+1-H>^+21!UDMn43PhYim1ss+97s&v|z{_gPU{`}uacB^V5LiuYr&7KZ%Y zj%}CMbjfWXr5Ov)jqUTIWr=IAd|3Ewaw=@teA$mBs_;Qg{X)|fkJ@|zX@;gt31kx8 zYLy<}u_i_8{>8*f4Z?k#@!Bi~8>WC2I>aQXVn;Mw{N}<1d2o*O;bw);ifkWEFe2Az z{|LX0MGD+o&H69uv>uZ8!NO7Z9$$7_0@voyIQ*Np#w3i?p>#2;s@vp5pnZ)GbMR*_p^5kW;p(5gU=xwOPizDcSd0L(2jk2RMw)n zi3{|3K+$8*Qew`hq7UdyonxPW3|d4^CDY6JOaSU`Fbxey=n40>Ptk2nB%M$B zlA)rlgPWH9`~*YGl6Q!@AFrIxW{N0V^Xts1WV%~%BX+E5{^_q0&lfAeOrF$?rdlgH zNZaSXZQCP?F9QzJ@PYG+#Pq)dd}^^`bfjdNoLsD_YYnOueN|$%w29qm87~+sN4ibK z80Sdk^kL#tn}_|{p*nWlV+pqaSD-6>l5;^Vk3mE;YhuSy^FbKXv)f8%cG%yUcvEV* zV>~)MUFtEsU-g5{O~1k8Z#0nc_VA0Sy{u~Dop1eQiq2uB0ZTWQ5sG=Ig?5ZX*Y?H$P_!;}T!_9t` zF6Z^E!>ueY!b)3=T(e;n)h`)v%;+ZrD=L@3;h@LkN{oZ%hiB;+l($x_J}qO$^h7@h zMI)EjfeHbU@OL{4Nbqo~BY{WE zB!~!}Wu|sg6LOV*3@f;CN~aP+IqkqG#AlGEn=d>4Bn3WD0ADDF0`|TK<`uD!gHtZ^ zJ4k35bm!XdZXZ5Y2n0;m9ZWF&jALzP4<3`J$8a0rj2_8<0WA~jz!CT;0F%l==}6T5 zab_+Pj9m&f{ftz}1p}706tf5@Gbg@9(g=F_kcVzNaD$Shkt{8K;Cj9sFw&J)PMDiU zil%(peKK9vFrW*4|3;0>y1raAF$~S|P2C zYnz?(am`z9|eu;IG) z^#{hUjTUHGO!}|`Sp<+s8o0!aGD?97zLXG!o_tMeNPLO7{WZ*()j3x%;R(g{8wxL; zEno6IW=rx(obAN$r^e&}fokU;ADqK0z0*E;krI3u`=88z?|VuUwAXy*9y65uNpyN2 zKVJaJ>tNinNX{KHqprJ<3BJ;u;! zd$Py^REEuG~?uGko+0-e@##uwc3eVNVqyuk96B5!|aM1 z7nxzf2f**&`(1iO!XY0ab#8*B1paUjVtdoU$5Y-~$ZoWpq7vGL`j^u5|Mz|R|5(yX zf#gcMT2T=u3+>(Px-&cvbR)LAUKJjg{>P(}jWi$E8jF-GEt|)-zxrz}6S!2WN*;FF zfIStSnp2N`?eWLEsI4}nlVa*1!_@T2Wbt%E%a&Y1GKBddNTAu$#SX)FSQ~&mSiFfI zf3ixv8AD(_LoeMw+ksPAo2eZO{;Eq(5oLCZ25h6ClO-rXkUP5YF}6Q}^onZA~%OP+1L6dPRLDkq9XoS^d~S z0B6C1pWi*A*rRoL@|N;%J|VB0L)9Pg>^(Jb5P|J&{SrXTe|r>< zj}v&Yd8tz@B;m* zUs)zAIdbYH%)qXQ6Uj;4hy4ZJD{IZ9w$<8>zh(-UtuWk$pRR#sv4ezi69N!6(= z8vpXTFjKyOYuo8=W9i>RF>WJ?(K#5~@Aj;7`5@sHFj$o&g+s$sa=f$TW!0JguYtAV z2#p&`$`jrb7{g|a=AFvzBQ=N8EZuU(iKDPfgz^1SrIZAIF=A&MqLHs<*CbS)^X7eT zcsc5><&FZrl`_gW^xW|gR_2jRu~C{o_;DPke|_?$-R0cluf%!+?*7b1OZbg}*TmV> zG}CMMY|?`=6Uce7?qSE!Qh4f4SK?p%*9fCU`BD4DF~vV0+pjh{YF=6ue^)?CHgC&w zX}MKWmv^>JLf#(O664FtJR^}`!=iUjWjY6&M?+laTCW3fI~MH)-CMrvIppVYLgpr` z+LY;QLW_(++R{evK9LODUMrF7CT}dOk4&Nq$*S9#t~7ee^&Z{3L2jJ(g9uP0{2_)< z7ymak!zwU9QcW4CH2){(Tx2Y1N49B0Z=Q zGv=MWTrF=kX>?oOdN0Dl?Q8sMD`08??w+S^NF8)mUucq4+S+#KkM1c*F$XFTl_0{< zpT&;7>&N;xFDsX6*Lk81-AWY4`Z)GS`craS>Zy}}I8Hjfgo3-VH}Cu6ua~jCm#%_e zJDdb0-*~S4y3;QC?hAqe{*uN5#Mf(+&^oD6uM(P<8j9RwC53Hm1dB(Jl2E2ZpvETUE)M`Z$OA z0-0qgzh$v(2jm>M$j&24i-f*vZS#7k7lk+An-L6nKm%?_3G|h>RZI=$?@a0dmb^BXC?3oZQSA?hzLO5X+4HG{M)Mw;A;!hjD|a2UT<$92^kV>qT^g&SgU2)93R7MY zi&UkCDL4vqv+2p@q&4eOWgC3q8t@?1b|5L@JsO~1bY0kzjk-7+Yn-mfJ1P5Y;>op_ zdu>{R>l_8gl9Ob-+)Ow5_S8* zQXf%jFEg$Pq)-mqVJQfzDr}s?ODM23JzxCp$~pS46PUWW&O^^Fm?h@rP@EmOxEHy;T<6bO_pnnliH&qVCedzoGo`&CM z@0GUNaYA7?8q*VCv1&_HS7P+4wR}IJYI`O)y|=H?bR(PsKKt{pI(Yfo;cluMva>^| zQdy~%!%G`z--_&z#2RZAgO)(`ZSI^g7hdAmZ4Bl#WC`|WVuM= zhmg;El5d3E*=aPKoTIO5`0kYy*7zOI7$UR+d_Rlqg-rHN5LZH(z%SQS^_;FantmXV zAMMJRBb?AT>q5T31Nqa{O6~2OqAu!%FqPJicQNo^5h;ZSLg|@@ON`9E~R8S>OC(HGiEN(ToR{?RiSdi!$K^&KIr__^@v)#f>>S13Zwwc zld}A<;70{vj{`d(Q}!Gw0EM=rkHl;*EOR-{z4~=b{r#TQpO5GP$Cfi-1YdS;Q;!1y zLzFJ~f6!Pkqkof+FEdJgF*w+JyZ4<+$^r*_i{5x<1_6jQzJ$svHk) zXYu%T3h}>zOaxx{Arj1-^Beq`$=QtG;A5g}`4e4-(A=k?ulhMr_1$kna)L`T?8i=y zJ|1!Hx;zk7%PeAavykDds+R&xgOhsg{g}W!S_!ZFv@2q>M$>M${Y(zY8gIn-p^`H? z61{vsao}}Zw3fHJdGQbTpFp4hQ{3IXl*U{eVuRRVnbU5MzSInqhN#_B4ihhpCwH32 z6>|vRO>Benv=M<)uk{u07`yB;MHc>TwnCTX$nQY#5tKsryrOqKTL_ authentication;inmemory;testing;cache;auth-framework - logo.png + uauthlogo.png README.md @@ -22,7 +22,7 @@ - + diff --git a/src/persistence/CodeBeam.UltimateAuth.InMemory/logo.png b/src/persistence/CodeBeam.UltimateAuth.InMemory/logo.png deleted file mode 100644 index aa51a469d9a9fdd0ff8ea6c0711ae28dafc8fb3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3551 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 L597+M377u|47W$d diff --git a/src/persistence/CodeBeam.UltimateAuth.InMemory/uauthlogo.png b/src/persistence/CodeBeam.UltimateAuth.InMemory/uauthlogo.png new file mode 100644 index 0000000000000000000000000000000000000000..911f2530a1353be33b73719b23571bd6edfb6e96 GIT binary patch literal 4954 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 authentication;authorization;policies;permissions;security;auth-framework - logo.png + uauthlogo.png README.md @@ -24,7 +24,7 @@ - + diff --git a/src/policies/CodeBeam.UltimateAuth.Policies/logo.png b/src/policies/CodeBeam.UltimateAuth.Policies/logo.png deleted file mode 100644 index aa51a469d9a9fdd0ff8ea6c0711ae28dafc8fb3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3551 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 L597+M377u|47W$d diff --git a/src/policies/CodeBeam.UltimateAuth.Policies/uauthlogo.png b/src/policies/CodeBeam.UltimateAuth.Policies/uauthlogo.png new file mode 100644 index 0000000000000000000000000000000000000000..911f2530a1353be33b73719b23571bd6edfb6e96 GIT binary patch literal 4954 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 authentication;security;argon2;password-hashing;cryptography;identity;auth-framework - logo.png + uauthlogo.png README.md @@ -26,7 +26,7 @@ - + diff --git a/src/security/CodeBeam.UltimateAuth.Security.Argon2/logo.png b/src/security/CodeBeam.UltimateAuth.Security.Argon2/logo.png deleted file mode 100644 index aa51a469d9a9fdd0ff8ea6c0711ae28dafc8fb3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3551 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 L597+M377u|47W$d diff --git a/src/security/CodeBeam.UltimateAuth.Security.Argon2/uauthlogo.png b/src/security/CodeBeam.UltimateAuth.Security.Argon2/uauthlogo.png new file mode 100644 index 0000000000000000000000000000000000000000..911f2530a1353be33b73719b23571bd6edfb6e96 GIT binary patch literal 4954 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 authentication;sessions;efcore;database;auth-framework - logo.png + uauthlogo.png README.md @@ -22,7 +22,7 @@ - + diff --git a/src/sessions/CodeBeam.UltimateAuth.Sessions.EntityFrameworkCore/logo.png b/src/sessions/CodeBeam.UltimateAuth.Sessions.EntityFrameworkCore/logo.png deleted file mode 100644 index aa51a469d9a9fdd0ff8ea6c0711ae28dafc8fb3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3551 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 L597+M377u|47W$d diff --git a/src/sessions/CodeBeam.UltimateAuth.Sessions.EntityFrameworkCore/uauthlogo.png b/src/sessions/CodeBeam.UltimateAuth.Sessions.EntityFrameworkCore/uauthlogo.png new file mode 100644 index 0000000000000000000000000000000000000000..911f2530a1353be33b73719b23571bd6edfb6e96 GIT binary patch literal 4954 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 authentication;sessions;inmemory;auth-framework - logo.png + uauthlogo.png README.md @@ -24,7 +24,7 @@ - + diff --git a/src/sessions/CodeBeam.UltimateAuth.Sessions.InMemory/logo.png b/src/sessions/CodeBeam.UltimateAuth.Sessions.InMemory/logo.png deleted file mode 100644 index aa51a469d9a9fdd0ff8ea6c0711ae28dafc8fb3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3551 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 L597+M377u|47W$d diff --git a/src/sessions/CodeBeam.UltimateAuth.Sessions.InMemory/uauthlogo.png b/src/sessions/CodeBeam.UltimateAuth.Sessions.InMemory/uauthlogo.png new file mode 100644 index 0000000000000000000000000000000000000000..911f2530a1353be33b73719b23571bd6edfb6e96 GIT binary patch literal 4954 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 z6>|vRO>Benv=M<)uk{u07`yB;MHc>TwnCTX$nQY#5tKsryrOqKTL_authentication;tokens;efcore;jwt;refresh-token;database;auth-framework README.md - logo.png + uauthlogo.png @@ -23,7 +23,7 @@ - + diff --git a/src/tokens/CodeBeam.UltimateAuth.Tokens.EntityFrameworkCore/logo.png b/src/tokens/CodeBeam.UltimateAuth.Tokens.EntityFrameworkCore/logo.png deleted file mode 100644 index aa51a469d9a9fdd0ff8ea6c0711ae28dafc8fb3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3551 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 L597+M377u|47W$d diff --git a/src/tokens/CodeBeam.UltimateAuth.Tokens.EntityFrameworkCore/uauthlogo.png b/src/tokens/CodeBeam.UltimateAuth.Tokens.EntityFrameworkCore/uauthlogo.png new file mode 100644 index 0000000000000000000000000000000000000000..911f2530a1353be33b73719b23571bd6edfb6e96 GIT binary patch literal 4954 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 authentication;tokens;inmemory;jwt;refresh-token;auth-framework - logo.png + uauthlogo.png README.md @@ -23,7 +23,7 @@ - + diff --git a/src/tokens/CodeBeam.UltimateAuth.Tokens.InMemory/logo.png b/src/tokens/CodeBeam.UltimateAuth.Tokens.InMemory/logo.png deleted file mode 100644 index aa51a469d9a9fdd0ff8ea6c0711ae28dafc8fb3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3551 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 L597+M377u|47W$d diff --git a/src/tokens/CodeBeam.UltimateAuth.Tokens.InMemory/uauthlogo.png b/src/tokens/CodeBeam.UltimateAuth.Tokens.InMemory/uauthlogo.png new file mode 100644 index 0000000000000000000000000000000000000000..911f2530a1353be33b73719b23571bd6edfb6e96 GIT binary patch literal 4954 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 authentication;identity;users;contracts;shared;dto;auth-framework - logo.png + uauthlogo.png README.md @@ -22,7 +22,7 @@ - + diff --git a/src/users/CodeBeam.UltimateAuth.Users.Contracts/logo.png b/src/users/CodeBeam.UltimateAuth.Users.Contracts/logo.png deleted file mode 100644 index aa51a469d9a9fdd0ff8ea6c0711ae28dafc8fb3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3551 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 L597+M377u|47W$d diff --git a/src/users/CodeBeam.UltimateAuth.Users.Contracts/uauthlogo.png b/src/users/CodeBeam.UltimateAuth.Users.Contracts/uauthlogo.png new file mode 100644 index 0000000000000000000000000000000000000000..911f2530a1353be33b73719b23571bd6edfb6e96 GIT binary patch literal 4954 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 authentication;users;efcore;database;sql;auth-framework - logo.png + uauthlogo.png README.md @@ -23,7 +23,7 @@ - + diff --git a/src/users/CodeBeam.UltimateAuth.Users.EntityFrameworkCore/logo.png b/src/users/CodeBeam.UltimateAuth.Users.EntityFrameworkCore/logo.png deleted file mode 100644 index aa51a469d9a9fdd0ff8ea6c0711ae28dafc8fb3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3551 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 L597+M377u|47W$d diff --git a/src/users/CodeBeam.UltimateAuth.Users.EntityFrameworkCore/uauthlogo.png b/src/users/CodeBeam.UltimateAuth.Users.EntityFrameworkCore/uauthlogo.png new file mode 100644 index 0000000000000000000000000000000000000000..911f2530a1353be33b73719b23571bd6edfb6e96 GIT binary patch literal 4954 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 authentication;users;inmemory;testing;auth-framework - logo.png + uauthlogo.png README.md @@ -25,7 +25,7 @@ - + diff --git a/src/users/CodeBeam.UltimateAuth.Users.InMemory/logo.png b/src/users/CodeBeam.UltimateAuth.Users.InMemory/logo.png deleted file mode 100644 index aa51a469d9a9fdd0ff8ea6c0711ae28dafc8fb3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3551 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 L597+M377u|47W$d diff --git a/src/users/CodeBeam.UltimateAuth.Users.InMemory/uauthlogo.png b/src/users/CodeBeam.UltimateAuth.Users.InMemory/uauthlogo.png new file mode 100644 index 0000000000000000000000000000000000000000..911f2530a1353be33b73719b23571bd6edfb6e96 GIT binary patch literal 4954 zcmcgwWmFVEw8j;Xg(a76mW8EDLILSmU};dg1$nfzN~|mb0>UDKq##I2hf7FENJvPt zbgXnOpwe&s`ObOq@BNs$_sq;W_s-n8-*;!?j106X$yv#Xh=?e4;2I_bdHAo9krH|( z^Q{zu0Q$l${fUSu=>D}^V+-MzL_}baj)tmPNcLWiz9+pl>nr8aU3V2q?ymO^#*<)B?F#;?x@e z)9+HyW~+1-H>^+21!UDMn43PhYim1ss+97s&v|z{_gPU{`}uacB^V5LiuYr&7KZ%Y zj%}CMbjfWXr5Ov)jqUTIWr=IAd|3Ewaw=@teA$mBs_;Qg{X)|fkJ@|zX@;gt31kx8 zYLy<}u_i_8{>8*f4Z?k#@!Bi~8>WC2I>aQXVn;Mw{N}<1d2o*O;bw);ifkWEFe2Az z{|LX0MGD+o&H69uv>uZ8!NO7Z9$$7_0@voyIQ*Np#w3i?p>#2;s@vp5pnZ)GbMR*_p^5kW;p(5gU=xwOPizDcSd0L(2jk2RMw)n zi3{|3K+$8*Qew`hq7UdyonxPW3|d4^CDY6JOaSU`Fbxey=n40>Ptk2nB%M$B zlA)rlgPWH9`~*YGl6Q!@AFrIxW{N0V^Xts1WV%~%BX+E5{^_q0&lfAeOrF$?rdlgH zNZaSXZQCP?F9QzJ@PYG+#Pq)dd}^^`bfjdNoLsD_YYnOueN|$%w29qm87~+sN4ibK z80Sdk^kL#tn}_|{p*nWlV+pqaSD-6>l5;^Vk3mE;YhuSy^FbKXv)f8%cG%yUcvEV* zV>~)MUFtEsU-g5{O~1k8Z#0nc_VA0Sy{u~Dop1eQiq2uB0ZTWQ5sG=Ig?5ZX*Y?H$P_!;}T!_9t` zF6Z^E!>ueY!b)3=T(e;n)h`)v%;+ZrD=L@3;h@LkN{oZ%hiB;+l($x_J}qO$^h7@h zMI)EjfeHbU@OL{4Nbqo~BY{WE zB!~!}Wu|sg6LOV*3@f;CN~aP+IqkqG#AlGEn=d>4Bn3WD0ADDF0`|TK<`uD!gHtZ^ zJ4k35bm!XdZXZ5Y2n0;m9ZWF&jALzP4<3`J$8a0rj2_8<0WA~jz!CT;0F%l==}6T5 zab_+Pj9m&f{ftz}1p}706tf5@Gbg@9(g=F_kcVzNaD$Shkt{8K;Cj9sFw&J)PMDiU zil%(peKK9vFrW*4|3;0>y1raAF$~S|P2C zYnz?(am`z9|eu;IG) z^#{hUjTUHGO!}|`Sp<+s8o0!aGD?97zLXG!o_tMeNPLO7{WZ*()j3x%;R(g{8wxL; zEno6IW=rx(obAN$r^e&}fokU;ADqK0z0*E;krI3u`=88z?|VuUwAXy*9y65uNpyN2 zKVJaJ>tNinNX{KHqprJ<3BJ;u;! zd$Py^REEuG~?uGko+0-e@##uwc3eVNVqyuk96B5!|aM1 z7nxzf2f**&`(1iO!XY0ab#8*B1paUjVtdoU$5Y-~$ZoWpq7vGL`j^u5|Mz|R|5(yX zf#gcMT2T=u3+>(Px-&cvbR)LAUKJjg{>P(}jWi$E8jF-GEt|)-zxrz}6S!2WN*;FF zfIStSnp2N`?eWLEsI4}nlVa*1!_@T2Wbt%E%a&Y1GKBddNTAu$#SX)FSQ~&mSiFfI zf3ixv8AD(_LoeMw+ksPAo2eZO{;Eq(5oLCZ25h6ClO-rXkUP5YF}6Q}^onZA~%OP+1L6dPRLDkq9XoS^d~S z0B6C1pWi*A*rRoL@|N;%J|VB0L)9Pg>^(Jb5P|J&{SrXTe|r>< zj}v&Yd8tz@B;m* zUs)zAIdbYH%)qXQ6Uj;4hy4ZJD{IZ9w$<8>zh(-UtuWk$pRR#sv4ezi69N!6(= z8vpXTFjKyOYuo8=W9i>RF>WJ?(K#5~@Aj;7`5@sHFj$o&g+s$sa=f$TW!0JguYtAV z2#p&`$`jrb7{g|a=AFvzBQ=N8EZuU(iKDPfgz^1SrIZAIF=A&MqLHs<*CbS)^X7eT zcsc5><&FZrl`_gW^xW|gR_2jRu~C{o_;DPke|_?$-R0cluf%!+?*7b1OZbg}*TmV> zG}CMMY|?`=6Uce7?qSE!Qh4f4SK?p%*9fCU`BD4DF~vV0+pjh{YF=6ue^)?CHgC&w zX}MKWmv^>JLf#(O664FtJR^}`!=iUjWjY6&M?+laTCW3fI~MH)-CMrvIppVYLgpr` z+LY;QLW_(++R{evK9LODUMrF7CT}dOk4&Nq$*S9#t~7ee^&Z{3L2jJ(g9uP0{2_)< z7ymak!zwU9QcW4CH2){(Tx2Y1N49B0Z=Q zGv=MWTrF=kX>?oOdN0Dl?Q8sMD`08??w+S^NF8)mUucq4+S+#KkM1c*F$XFTl_0{< zpT&;7>&N;xFDsX6*Lk81-AWY4`Z)GS`craS>Zy}}I8Hjfgo3-VH}Cu6ua~jCm#%_e zJDdb0-*~S4y3;QC?hAqe{*uN5#Mf(+&^oD6uM(P<8j9RwC53Hm1dB(Jl2E2ZpvETUE)M`Z$OA z0-0qgzh$v(2jm>M$j&24i-f*vZS#7k7lk+An-L6nKm%?_3G|h>RZI=$?@a0dmb^BXC?3oZQSA?hzLO5X+4HG{M)Mw;A;!hjD|a2UT<$92^kV>qT^g&SgU2)93R7MY zi&UkCDL4vqv+2p@q&4eOWgC3q8t@?1b|5L@JsO~1bY0kzjk-7+Yn-mfJ1P5Y;>op_ zdu>{R>l_8gl9Ob-+)Ow5_S8* zQXf%jFEg$Pq)-mqVJQfzDr}s?ODM23JzxCp$~pS46PUWW&O^^Fm?h@rP@EmOxEHy;T<6bO_pnnliH&qVCedzoGo`&CM z@0GUNaYA7?8q*VCv1&_HS7P+4wR}IJYI`O)y|=H?bR(PsKKt{pI(Yfo;cluMva>^| zQdy~%!%G`z--_&z#2RZAgO)(`ZSI^g7hdAmZ4Bl#WC`|WVuM= zhmg;El5d3E*=aPKoTIO5`0kYy*7zOI7$UR+d_Rlqg-rHN5LZH(z%SQS^_;FantmXV zAMMJRBb?AT>q5T31Nqa{O6~2OqAu!%FqPJicQNo^5h;ZSLg|@@ON`9E~R8S>OC(HGiEN(ToR{?RiSdi!$K^&KIr__^@v)#f>>S13Zwwc zld}A<;70{vj{`d(Q}!Gw0EM=rkHl;*EOR-{z4~=b{r#TQpO5GP$Cfi-1YdS;Q;!1y zLzFJ~f6!Pkqkof+FEdJgF*w+JyZ4<+$^r*_i{5x<1_6jQzJ$svHk) zXYu%T3h}>zOaxx{Arj1-^Beq`$=QtG;A5g}`4e4-(A=k?ulhMr_1$kna)L`T?8i=y zJ|1!Hx;zk7%PeAavykDds+R&xgOhsg{g}W!S_!ZFv@2q>M$>M${Y(zY8gIn-p^`H? z61{vsao}}Zw3fHJdGQbTpFp4hQ{3IXl*U{eVuRRVnbU5MzSInqhN#_B4ihhpCwH32 z6>|vRO>Benv=M<)uk{u07`yB;MHc>TwnCTX$nQY#5tKsryrOqKTL_ authentication;identity;users;reference;plugin;auth-framework - logo.png + uauthlogo.png README.md @@ -24,7 +24,7 @@ - + diff --git a/src/users/CodeBeam.UltimateAuth.Users.Reference/logo.png b/src/users/CodeBeam.UltimateAuth.Users.Reference/logo.png deleted file mode 100644 index aa51a469d9a9fdd0ff8ea6c0711ae28dafc8fb3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3551 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 L597+M377u|47W$d diff --git a/src/users/CodeBeam.UltimateAuth.Users.Reference/uauthlogo.png b/src/users/CodeBeam.UltimateAuth.Users.Reference/uauthlogo.png new file mode 100644 index 0000000000000000000000000000000000000000..911f2530a1353be33b73719b23571bd6edfb6e96 GIT binary patch literal 4954 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 authentication;identity;users;module;auth-framework - logo.png + uauthlogo.png README.md @@ -24,7 +24,7 @@ - + diff --git a/src/users/CodeBeam.UltimateAuth.Users/logo.png b/src/users/CodeBeam.UltimateAuth.Users/logo.png deleted file mode 100644 index aa51a469d9a9fdd0ff8ea6c0711ae28dafc8fb3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3551 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 L597+M377u|47W$d diff --git a/src/users/CodeBeam.UltimateAuth.Users/uauthlogo.png b/src/users/CodeBeam.UltimateAuth.Users/uauthlogo.png new file mode 100644 index 0000000000000000000000000000000000000000..911f2530a1353be33b73719b23571bd6edfb6e96 GIT binary patch literal 4954 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 Date: Thu, 9 Apr 2026 00:32:04 +0300 Subject: [PATCH 2/4] Docs Content On Website --- UltimateAuth.slnx | 3 + docs/content/_groups.json | 9 + docs/content/auth-flows/device-management.md | 7 + docs/content/auth-flows/index.md | 7 + docs/content/auth-flows/login-flow.md | 7 + docs/content/auth-flows/logout-flow.md | 7 + docs/content/auth-flows/refresh-flow.md | 7 + docs/content/auth-flows/session-lifecycle.md | 7 + docs/content/auth-flows/token-behavior.md | 7 + docs/content/client/authentication.md | 7 + docs/content/client/authorization.md | 7 + docs/content/client/credentials.md | 7 + docs/content/client/identifiers.md | 7 + docs/content/client/index.md | 7 + docs/content/client/session-management.md | 7 + docs/content/client/user-management.md | 7 + .../configuration/advanced-configuration.md | 7 + docs/content/configuration/client-options.md | 7 + .../configuration/configuration-overview.md | 8 +- .../configuration/configuration-sources.md | 7 + docs/content/configuration/index.md | 7 + docs/content/configuration/server-options.md | 7 + docs/content/fundamentals/auth-model.md | 7 + docs/content/fundamentals/auth-modes.md | 7 + docs/content/fundamentals/client-profiles.md | 7 + docs/content/fundamentals/flow-based-auth.md | 7 + docs/content/fundamentals/index.md | 40 ++- .../content/fundamentals/request-lifecycle.md | 7 + .../fundamentals/runtime-architecture.md | 7 + docs/content/getting-started/index.md | 6 + docs/content/getting-started/quickstart.md | 6 + .../getting-started/real-world-setup.md | 6 + .../plugin-domains/authorization-domain.md | 7 + .../plugin-domains/credential-domain.md | 7 + docs/content/plugin-domains/index.md | 7 + docs/content/plugin-domains/policies.md | 7 + docs/content/plugin-domains/users-domain.md | 7 + .../content/security/access-token-behavior.md | 7 + docs/content/security/policy-pipeline.md | 7 + docs/content/security/refresh-rotation.md | 7 + .../security/session-security-model.md | 7 + ...eBeam.UltimateAuth.Docs.Wasm.Client.csproj | 7 + .../Layout/DocsLayout.razor | 13 + .../Layout/MainLayout.razor | 2 +- .../Pages/DocsContent.razor | 78 +++++ .../Pages/DocsLandingPage.razor | 198 ++++++++++-- .../Pages/DocsPage.razor | 9 + .../Pages/DocsSidebar.razor | 79 +++++ .../Program.cs | 6 + .../docs/auth-flows/device-management.json | 5 + .../wwwroot/docs/auth-flows/index.json | 5 + .../wwwroot/docs/auth-flows/login-flow.json | 5 + .../wwwroot/docs/auth-flows/logout-flow.json | 5 + .../wwwroot/docs/auth-flows/refresh-flow.json | 5 + .../docs/auth-flows/session-lifecycle.json | 5 + .../docs/auth-flows/token-behavior.json | 5 + .../wwwroot/docs/client/authentication.json | 5 + .../wwwroot/docs/client/authorization.json | 5 + .../wwwroot/docs/client/credentials.json | 5 + .../wwwroot/docs/client/identifiers.json | 5 + .../wwwroot/docs/client/index.json | 5 + .../docs/client/session-management.json | 5 + .../wwwroot/docs/client/user-management.json | 5 + .../configuration/advanced-configuration.json | 5 + .../docs/configuration/client-options.json | 5 + .../configuration/configuration-overview.json | 5 + .../configuration/configuration-sources.json | 5 + .../wwwroot/docs/configuration/index.json | 5 + .../docs/configuration/server-options.json | 5 + .../wwwroot/docs/docs-index.json | 282 ++++++++++++++++++ .../wwwroot/docs/fundamentals/auth-model.json | 5 + .../wwwroot/docs/fundamentals/auth-modes.json | 5 + .../docs/fundamentals/client-profiles.json | 5 + .../docs/fundamentals/flow-based-auth.json | 5 + .../wwwroot/docs/fundamentals/index.json | 5 + .../docs/fundamentals/request-lifecycle.json | 5 + .../fundamentals/runtime-architecture.json | 5 + .../wwwroot/docs/getting-started/index.json | 5 + .../docs/getting-started/quickstart.json | 5 + .../getting-started/real-world-setup.json | 5 + .../plugin-domains/authorization-domain.json | 5 + .../plugin-domains/credential-domain.json | 5 + .../wwwroot/docs/plugin-domains/index.json | 5 + .../wwwroot/docs/plugin-domains/policies.json | 5 + .../docs/plugin-domains/users-domain.json | 5 + .../wwwroot/docs/readme.json | 5 + .../docs/security/access-token-behavior.json | 5 + .../docs/security/policy-pipeline.json | 5 + .../docs/security/refresh-rotation.json | 5 + .../docs/security/session-security-model.json | 5 + .../Program.cs | 2 + .../wwwroot/app.css | 57 ++++ .../CodeBeam.UltimateAuth.DocsBuilder.csproj | 15 + .../Program.cs | 250 ++++++++++++++++ 94 files changed, 1480 insertions(+), 34 deletions(-) create mode 100644 docs/content/_groups.json create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/DocsLayout.razor create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsContent.razor create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsPage.razor create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsSidebar.razor create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/device-management.json create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/index.json create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/login-flow.json create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/logout-flow.json create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/refresh-flow.json create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/session-lifecycle.json create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/token-behavior.json create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/authentication.json create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/authorization.json create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/credentials.json create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/identifiers.json create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/index.json create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/session-management.json create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/user-management.json create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/advanced-configuration.json create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/client-options.json create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/configuration-overview.json create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/configuration-sources.json create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/index.json create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/server-options.json create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/docs-index.json create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/auth-model.json create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/auth-modes.json create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/client-profiles.json create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/flow-based-auth.json create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/index.json create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/request-lifecycle.json create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/runtime-architecture.json create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/index.json create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/quickstart.json create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/real-world-setup.json create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/authorization-domain.json create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/credential-domain.json create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/index.json create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/policies.json create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/users-domain.json create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/readme.json create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/access-token-behavior.json create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/policy-pipeline.json create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/refresh-rotation.json create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/session-security-model.json create mode 100644 src/utilities/CodeBeam.UltimateAuth.DocsBuilder/CodeBeam.UltimateAuth.DocsBuilder.csproj create mode 100644 src/utilities/CodeBeam.UltimateAuth.DocsBuilder/Program.cs diff --git a/UltimateAuth.slnx b/UltimateAuth.slnx index 4d101bde..c449693f 100644 --- a/UltimateAuth.slnx +++ b/UltimateAuth.slnx @@ -25,6 +25,9 @@ + + + diff --git a/docs/content/_groups.json b/docs/content/_groups.json new file mode 100644 index 00000000..bae8f92e --- /dev/null +++ b/docs/content/_groups.json @@ -0,0 +1,9 @@ +{ + "getting-started": 1, + "fundamentals": 2, + "auth-flows": 3, + "plugin-domains": 4, + "client": 5, + "configuration": 6, + "security": 7 +} \ No newline at end of file diff --git a/docs/content/auth-flows/device-management.md b/docs/content/auth-flows/device-management.md index 9e9305bf..a2184b8f 100644 --- a/docs/content/auth-flows/device-management.md +++ b/docs/content/auth-flows/device-management.md @@ -1,3 +1,10 @@ +--- +title: Device Management +order: 7 +group: auth-flows +--- + + # 📱 Device Management In UltimateAuth, devices are not an afterthought. diff --git a/docs/content/auth-flows/index.md b/docs/content/auth-flows/index.md index 94d06100..4db4c8b3 100644 --- a/docs/content/auth-flows/index.md +++ b/docs/content/auth-flows/index.md @@ -1,3 +1,10 @@ +--- +title: Auth Flows +order: 1 +group: auth-flows +--- + + # 🔐 Auth Flows Authentication in UltimateAuth is not a single operation. diff --git a/docs/content/auth-flows/login-flow.md b/docs/content/auth-flows/login-flow.md index c5913c3f..0df83bb1 100644 --- a/docs/content/auth-flows/login-flow.md +++ b/docs/content/auth-flows/login-flow.md @@ -1,3 +1,10 @@ +--- +title: Login +order: 2 +group: auth-flows +--- + + # 🔑 Login Flow The login flow in UltimateAuth is not just credential validation. diff --git a/docs/content/auth-flows/logout-flow.md b/docs/content/auth-flows/logout-flow.md index f6b933f0..74d0eec7 100644 --- a/docs/content/auth-flows/logout-flow.md +++ b/docs/content/auth-flows/logout-flow.md @@ -1,3 +1,10 @@ +--- +title: Logout +order: 4 +group: auth-flows +--- + + # 🚪 Logout Flow The logout flow in UltimateAuth is not a single action. diff --git a/docs/content/auth-flows/refresh-flow.md b/docs/content/auth-flows/refresh-flow.md index 52ca0a68..9ff51683 100644 --- a/docs/content/auth-flows/refresh-flow.md +++ b/docs/content/auth-flows/refresh-flow.md @@ -1,3 +1,10 @@ +--- +title: Refresh +order: 3 +group: auth-flows +--- + + # 🔄 Refresh Flow The refresh flow in UltimateAuth is not a single fixed operation. diff --git a/docs/content/auth-flows/session-lifecycle.md b/docs/content/auth-flows/session-lifecycle.md index f628bab8..6f79a04c 100644 --- a/docs/content/auth-flows/session-lifecycle.md +++ b/docs/content/auth-flows/session-lifecycle.md @@ -1,3 +1,10 @@ +--- +title: Session Lifecycle +order: 5 +group: auth-flows +--- + + # 🧬 Session Lifecycle UltimateAuth is built around a structured session model. diff --git a/docs/content/auth-flows/token-behavior.md b/docs/content/auth-flows/token-behavior.md index f98d056e..18c76126 100644 --- a/docs/content/auth-flows/token-behavior.md +++ b/docs/content/auth-flows/token-behavior.md @@ -1,3 +1,10 @@ +--- +title: Token Behavior +order: 6 +group: auth-flows +--- + + # 🎟 Token Behavior In UltimateAuth, tokens are not the foundation of authentication. diff --git a/docs/content/client/authentication.md b/docs/content/client/authentication.md index 4148c02d..53630cdf 100644 --- a/docs/content/client/authentication.md +++ b/docs/content/client/authentication.md @@ -1,3 +1,10 @@ +--- +title: Authentication +order: 2 +group: client +--- + + # 🔐 Authentication Guide This section explains how to use the UltimateAuth client for authentication flows. diff --git a/docs/content/client/authorization.md b/docs/content/client/authorization.md index 8880bc18..b3c4a880 100644 --- a/docs/content/client/authorization.md +++ b/docs/content/client/authorization.md @@ -1,3 +1,10 @@ +--- +title: Authorization +order: 7 +group: client +--- + + # 🛡 Authorization Guide This section explains how to manage roles, permissions, and access control using the UltimateAuth client. diff --git a/docs/content/client/credentials.md b/docs/content/client/credentials.md index 0df5b06c..6ce81fb6 100644 --- a/docs/content/client/credentials.md +++ b/docs/content/client/credentials.md @@ -1,3 +1,10 @@ +--- +title: Credentials +order: 6 +group: client +--- + + # 🔑 Credential Management Guide This section explains how to manage user credentials (such as passwords) using the UltimateAuth client. diff --git a/docs/content/client/identifiers.md b/docs/content/client/identifiers.md index 0629a49e..ead1b971 100644 --- a/docs/content/client/identifiers.md +++ b/docs/content/client/identifiers.md @@ -1,3 +1,10 @@ +--- +title: Identifiers +order: 5 +group: client +--- + + # 🆔 User Identifiers Guide This section explains how UltimateAuth manages user identifiers such as email, username, and phone. diff --git a/docs/content/client/index.md b/docs/content/client/index.md index a933839f..7f75a687 100644 --- a/docs/content/client/index.md +++ b/docs/content/client/index.md @@ -1,3 +1,10 @@ +--- +title: Client & API +order: 1 +group: client +--- + + # 🚀 Client Usage Guide UltimateAuth Client is a **high-level SDK** designed to simplify authentication flows. diff --git a/docs/content/client/session-management.md b/docs/content/client/session-management.md index d5885e9a..67e86767 100644 --- a/docs/content/client/session-management.md +++ b/docs/content/client/session-management.md @@ -1,3 +1,10 @@ +--- +title: Session Management +order: 3 +group: client +--- + + # 📱 Session Management Guide This section explains how to manage sessions and devices using the UltimateAuth client. diff --git a/docs/content/client/user-management.md b/docs/content/client/user-management.md index 53df4f0a..f6c7c72e 100644 --- a/docs/content/client/user-management.md +++ b/docs/content/client/user-management.md @@ -1,3 +1,10 @@ +--- +title: User Management +order: 4 +group: client +--- + + # 👤 User Management Guide This section explains how to manage users using the UltimateAuth client. diff --git a/docs/content/configuration/advanced-configuration.md b/docs/content/configuration/advanced-configuration.md index 39b6815a..7ff33835 100644 --- a/docs/content/configuration/advanced-configuration.md +++ b/docs/content/configuration/advanced-configuration.md @@ -1,3 +1,10 @@ +--- +title: Advanced Configuration +order: 6 +group: configuration +--- + + # 🧠 Advanced Configuration UltimateAuth is designed to be flexible — but not fragile. diff --git a/docs/content/configuration/client-options.md b/docs/content/configuration/client-options.md index ce417097..4ce9c7c3 100644 --- a/docs/content/configuration/client-options.md +++ b/docs/content/configuration/client-options.md @@ -1,3 +1,10 @@ +--- +title: Client Options +order: 4 +group: configuration +--- + + # 🧩 Client Options Client Options define how UltimateAuth behaves on the **client side**. diff --git a/docs/content/configuration/configuration-overview.md b/docs/content/configuration/configuration-overview.md index 50767eb7..fe9bd7a7 100644 --- a/docs/content/configuration/configuration-overview.md +++ b/docs/content/configuration/configuration-overview.md @@ -1,3 +1,10 @@ +--- +title: Configuration Overview +order: 2 +group: configuration +--- + + # 🧠 Configuration Overview UltimateAuth is not configured as a static system. @@ -161,6 +168,5 @@ If you remember one thing: ## ➡️ Next Step -- Deep dive into behavior → Core Options - Control runtime → Server Options - Configure clients → Client Options diff --git a/docs/content/configuration/configuration-sources.md b/docs/content/configuration/configuration-sources.md index 964efc8e..3386a379 100644 --- a/docs/content/configuration/configuration-sources.md +++ b/docs/content/configuration/configuration-sources.md @@ -1,3 +1,10 @@ +--- +title: Configuration Sources +order: 5 +group: configuration +--- + + # ⚙️ Configuration Sources UltimateAuth supports multiple configuration sources. diff --git a/docs/content/configuration/index.md b/docs/content/configuration/index.md index 534552eb..2aff7b31 100644 --- a/docs/content/configuration/index.md +++ b/docs/content/configuration/index.md @@ -1,3 +1,10 @@ +--- +title: Configuration +order: 1 +group: configuration +--- + + # ⚙️ Configuration & Extensibility UltimateAuth is designed to be flexible. diff --git a/docs/content/configuration/server-options.md b/docs/content/configuration/server-options.md index 1ad00e20..1656c29a 100644 --- a/docs/content/configuration/server-options.md +++ b/docs/content/configuration/server-options.md @@ -1,3 +1,10 @@ +--- +title: Server Options +order: 3 +group: configuration +--- + + # 🧩 Server Options UltimateAuth is configured primarily through **Server Options**. diff --git a/docs/content/fundamentals/auth-model.md b/docs/content/fundamentals/auth-model.md index b5026353..0616637f 100644 --- a/docs/content/fundamentals/auth-model.md +++ b/docs/content/fundamentals/auth-model.md @@ -1,3 +1,10 @@ +--- +title: Authentication Model +order: 2 +group: fundamentals +--- + + # 🧠 Authentication Model UltimateAuth is built around a simple but powerful idea: diff --git a/docs/content/fundamentals/auth-modes.md b/docs/content/fundamentals/auth-modes.md index 941d14e3..756c202f 100644 --- a/docs/content/fundamentals/auth-modes.md +++ b/docs/content/fundamentals/auth-modes.md @@ -1,3 +1,10 @@ +--- +title: Authentication Modes +order: 4 +group: fundamentals +--- + + > Note: SemiHybrid and PureJwt modes will be available on future releases. For now you can safely use PureOpaque and Hybrid modes. # 🔐 Authentication Modes diff --git a/docs/content/fundamentals/client-profiles.md b/docs/content/fundamentals/client-profiles.md index 7c4387fa..cfa72dd0 100644 --- a/docs/content/fundamentals/client-profiles.md +++ b/docs/content/fundamentals/client-profiles.md @@ -1,3 +1,10 @@ +--- +title: Client Profiles +order: 5 +group: fundamentals +--- + + # 🧩 Client Profiles UltimateAuth adapts its authentication behavior based on the client. diff --git a/docs/content/fundamentals/flow-based-auth.md b/docs/content/fundamentals/flow-based-auth.md index ba4081dc..6e60d06a 100644 --- a/docs/content/fundamentals/flow-based-auth.md +++ b/docs/content/fundamentals/flow-based-auth.md @@ -1,3 +1,10 @@ +--- +title: Flow Based Authentication +order: 3 +group: fundamentals +--- + + # 🔄 Flow-Based Authentication UltimateAuth is not cookie-based or token-based. diff --git a/docs/content/fundamentals/index.md b/docs/content/fundamentals/index.md index cd2cfed7..1da5c3d3 100644 --- a/docs/content/fundamentals/index.md +++ b/docs/content/fundamentals/index.md @@ -1,12 +1,38 @@ -# 🧠 Fundamentals +\--- + +title: Fundamental Overview + +order: 1 + +group: fundamentals + +\--- + + + + + +\# 🧠 Fundamentals + + This section explains how UltimateAuth works internally. + + If you are new, follow this order: -1. [Authentication Model](./auth-model.md) -2. [Flow-Based Authentication](./flow-based-auth.md) -3. [Authentication Modes](./auth-modes.md) -4. [Client Profiles](./client-profiles.md) -5. [Runtime Architecture](./runtime-architecture.md) -6. [Request Lifecycle](./request-lifecycle.md) + + +\- Authentication Model + +\- Flow-Based Authentication + +\- Authentication Modes + +\- Client Profiles + +\- Runtime Architecture + +\- Request Lifecycle + diff --git a/docs/content/fundamentals/request-lifecycle.md b/docs/content/fundamentals/request-lifecycle.md index 4cf2cf34..94ca5f76 100644 --- a/docs/content/fundamentals/request-lifecycle.md +++ b/docs/content/fundamentals/request-lifecycle.md @@ -1,3 +1,10 @@ +--- +title: Request Lifecycle +order: 7 +group: fundamentals +--- + + # 🔄 Request Lifecycle This section explains what happens when a request enters UltimateAuth. diff --git a/docs/content/fundamentals/runtime-architecture.md b/docs/content/fundamentals/runtime-architecture.md index 8d857469..992ed5a7 100644 --- a/docs/content/fundamentals/runtime-architecture.md +++ b/docs/content/fundamentals/runtime-architecture.md @@ -1,3 +1,10 @@ +--- +title: Runtime Architecture +order: 6 +group: fundamentals +--- + + # 🏗 Runtime Architecture UltimateAuth processes authentication through a structured execution pipeline. diff --git a/docs/content/getting-started/index.md b/docs/content/getting-started/index.md index e8b7dcda..2fe4abd8 100644 --- a/docs/content/getting-started/index.md +++ b/docs/content/getting-started/index.md @@ -1,3 +1,9 @@ +--- +title: Getting Started +order: 1 +group: getting-started +--- + # 🚀 Getting Started Welcome to **UltimateAuth** — the modern authentication framework for .NET. diff --git a/docs/content/getting-started/quickstart.md b/docs/content/getting-started/quickstart.md index f6b5c27d..8d77e763 100644 --- a/docs/content/getting-started/quickstart.md +++ b/docs/content/getting-started/quickstart.md @@ -1,3 +1,9 @@ +--- +title: QuickStart +order: 2 +group: getting-started +--- + # ⚡ Quick Start In this guide, you will set up UltimateAuth in a few minutes and perform your **first login**. diff --git a/docs/content/getting-started/real-world-setup.md b/docs/content/getting-started/real-world-setup.md index 48578b07..574cf928 100644 --- a/docs/content/getting-started/real-world-setup.md +++ b/docs/content/getting-started/real-world-setup.md @@ -1,3 +1,9 @@ +--- +title: Real World Setup +order: 3 +group: getting-started +--- + # 🏗 Real-World Setup The Quick Start uses an in-memory setup for simplicity. diff --git a/docs/content/plugin-domains/authorization-domain.md b/docs/content/plugin-domains/authorization-domain.md index 735ab0e8..1b9e0c37 100644 --- a/docs/content/plugin-domains/authorization-domain.md +++ b/docs/content/plugin-domains/authorization-domain.md @@ -1,3 +1,10 @@ +--- +title: Authorization +order: 4 +group: plugin-domains +--- + + # 🔐 Authorization & Policies UltimateAuth provides a flexible and extensible authorization system based on: diff --git a/docs/content/plugin-domains/credential-domain.md b/docs/content/plugin-domains/credential-domain.md index 50a8c2e3..1f9f5813 100644 --- a/docs/content/plugin-domains/credential-domain.md +++ b/docs/content/plugin-domains/credential-domain.md @@ -1,3 +1,10 @@ +--- +title: Credentials +order: 3 +group: plugin-domains +--- + + # 🔐 Credentials Domain Credentials in UltimateAuth define how a user proves their identity. diff --git a/docs/content/plugin-domains/index.md b/docs/content/plugin-domains/index.md index 596d2e23..14950f03 100644 --- a/docs/content/plugin-domains/index.md +++ b/docs/content/plugin-domains/index.md @@ -1,3 +1,10 @@ +--- +title: Plugin Domains +order: 1 +group: plugin-domains +--- + + # 🧩 Plugin Domains Authentication alone is not enough. diff --git a/docs/content/plugin-domains/policies.md b/docs/content/plugin-domains/policies.md index 111f0bbe..b688e5b9 100644 --- a/docs/content/plugin-domains/policies.md +++ b/docs/content/plugin-domains/policies.md @@ -1,3 +1,10 @@ +--- +title: Policies +order: 5 +group: plugin-domains +--- + + # 🛡 Policies & Access Control UltimateAuth uses a **policy-driven authorization model**. diff --git a/docs/content/plugin-domains/users-domain.md b/docs/content/plugin-domains/users-domain.md index 45630abb..c7b27935 100644 --- a/docs/content/plugin-domains/users-domain.md +++ b/docs/content/plugin-domains/users-domain.md @@ -1,3 +1,10 @@ +--- +title: Users +order: 2 +group: plugin-domains +--- + + # 👤 Users Domain Users in UltimateAuth are not a single entity. diff --git a/docs/content/security/access-token-behavior.md b/docs/content/security/access-token-behavior.md index 4313693e..4920b808 100644 --- a/docs/content/security/access-token-behavior.md +++ b/docs/content/security/access-token-behavior.md @@ -1,3 +1,10 @@ +--- +title: Access Token Behavior +order: 2 +group: security +--- + + # 🎯 Access Token Behavior Access tokens in UltimateAuth are intentionally **short-lived and mode-aware**. diff --git a/docs/content/security/policy-pipeline.md b/docs/content/security/policy-pipeline.md index 1ff9fc78..f7d28ba3 100644 --- a/docs/content/security/policy-pipeline.md +++ b/docs/content/security/policy-pipeline.md @@ -1,3 +1,10 @@ +--- +title: Policy Pipeline +order: 4 +group: security +--- + + # 🧠 Policy Pipeline Deep Dive UltimateAuth does not rely on simple role checks. diff --git a/docs/content/security/refresh-rotation.md b/docs/content/security/refresh-rotation.md index 2db9b053..51c00e3f 100644 --- a/docs/content/security/refresh-rotation.md +++ b/docs/content/security/refresh-rotation.md @@ -1,3 +1,10 @@ +--- +title: Refresh Rotation +order: 3 +group: security +--- + + # 🔄 Refresh Token Rotation Refresh tokens in UltimateAuth are not simple long-lived tokens. diff --git a/docs/content/security/session-security-model.md b/docs/content/security/session-security-model.md index f61c00fd..e905d138 100644 --- a/docs/content/security/session-security-model.md +++ b/docs/content/security/session-security-model.md @@ -1,3 +1,10 @@ +--- +title: Session Security Model +order: 1 +group: security +--- + + # 🔐 Session Security Model UltimateAuth is built around a **hierarchical session model**. diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/CodeBeam.UltimateAuth.Docs.Wasm.Client.csproj b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/CodeBeam.UltimateAuth.Docs.Wasm.Client.csproj index bb9cbd6e..04640ad2 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/CodeBeam.UltimateAuth.Docs.Wasm.Client.csproj +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/CodeBeam.UltimateAuth.Docs.Wasm.Client.csproj @@ -17,4 +17,11 @@ + + + + diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/DocsLayout.razor b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/DocsLayout.razor new file mode 100644 index 00000000..7e6c42f1 --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/DocsLayout.razor @@ -0,0 +1,13 @@ +@using CodeBeam.UltimateAuth.Docs.Wasm.Client.Pages +@inherits LayoutComponentBase +@layout MainLayout + + + + + + + + @Body + + \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/MainLayout.razor b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/MainLayout.razor index 75526c72..c590d2f0 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/MainLayout.razor +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/MainLayout.razor @@ -18,7 +18,7 @@
- Docs + Docs Samples Donate
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsContent.razor b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsContent.razor new file mode 100644 index 00000000..5da05cd6 --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsContent.razor @@ -0,0 +1,78 @@ +@using System.Net.Http.Json +@inject HttpClient Http + +@if (_loading || !RendererInfo.IsInteractive) +{ +
+ +
+ return; +} +else if (_notFound) +{ + + + Document not found. + + +} +else if (_doc is not null) +{ + + @_doc.Title + + + +
@((MarkupString)_doc.Html)
+
+} + +@code { + [Parameter] + public string? Slug { get; set; } + + private DocPage? _doc; + private bool _loading = true; + private bool _notFound; + private ElementReference _contentRef; + + protected override async Task OnParametersSetAsync() + { + _loading = true; + _notFound = false; + _doc = null; + + var slug = string.IsNullOrWhiteSpace(Slug) + ? "index" + : Slug!.Trim('/'); + + try + { + _doc = await Http.GetFromJsonAsync($"docs/{slug}.json"); + + if (_doc == null) + _notFound = true; + } + catch + { + _notFound = true; + } + + _loading = false; + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (!_loading && _doc != null) + { + // await JS.InvokeVoidAsync("docsEnhance", _contentRef); + } + } + + private sealed class DocPage + { + public string Slug { get; set; } = ""; + public string Title { get; set; } = ""; + public string Html { get; set; } = ""; + } +} \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsLandingPage.razor b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsLandingPage.razor index be6a91bc..6c71a602 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsLandingPage.razor +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsLandingPage.razor @@ -1,30 +1,178 @@ @page "/docs" +@layout DocsLayout - - - UltimateAuth Docs - The modern way to understand authentication — unified, simple and powerful. - - - - This page is preparing. - - - But the documentation is currently available in markdown format and covers core concepts, architecture, flows and integration guides. - - - - Start exploring the docs to understand how UltimateAuth simplifies authentication across sessions, cookies and tokens. - - - - Read Documentation - - - - Full documentation site is coming soon. - + + + + + UltimateAuth Docs + The modern way to understand authentication — unified, simple and powerful. + + + Learn the architecture, flows, client APIs, security model, and integration patterns of UltimateAuth: + + + + + + + Start Here + + + + If you are new to UltimateAuth, start with Getting Started and + Fundamentals. If you care about usage, continue with Client & API. + If you are evaluating architecture or enterprise adoption, + continue with Auth Flows, Security, and Plugin Domains. + + + + + + + + + Getting Started + + + Set up UltimateAuth quickly, understand the first login flow, + and move into real-world application setup. + + + + Open Getting Started + + + + + + + + + Fundamentals + + + Understand the authentication model, flow-based execution, + auth modes, client profiles, and runtime architecture. + + + + Open Fundamentals + + + + + + + + + Auth Flows + + + Explore login, refresh, logout, session lifecycle, + token behavior, and device-aware authentication flows. + + + + Open Auth Flows + + + + + + + + + Client & API + + + Explore the usage, the service client layer of UltimateAuth. + Everything from basic login/logout to advanced session management should call with client. + + + + Open Client & API + + + + + + + + + Plugin Domains + + + Explore the user, credential, and authorization modules that not required for core Auth, but essential. + + + + Open Plugin Domains + + + + + + + + + + Security + + + + Review the session security model, token behavior, + refresh rotation, and the policy pipeline. + + + + Open Security Docs + + + + + + + + + + Coming Next + + + + The documentation experience will continue to expand with richer navigation, + improved code rendering, and dedicated reference surfaces. + + + + API Reference + Interactive authentication sandbox + Sample-driven guides + Integration recipes for Blazor, WASM, MAUI, MVC, and APIs + + + + + + + Browse Documentation + + + + View Markdown Source + + + - \ No newline at end of file + diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsPage.razor b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsPage.razor new file mode 100644 index 00000000..930e0873 --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsPage.razor @@ -0,0 +1,9 @@ +@page "/docs/{**slug}" +@layout DocsLayout + + + +@code { + [Parameter] + public string? slug { get; set; } +} diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsSidebar.razor b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsSidebar.razor new file mode 100644 index 00000000..d7bdc624 --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsSidebar.razor @@ -0,0 +1,79 @@ +@inject HttpClient Http +@inject NavigationManager Nav + +
+ + Documentation + + + + + @if (_groups == null) + { + + } + else + { + + @foreach (var group in _groups) + { + + @foreach (var item in group.Value) + { + @item.Title + } + + } + + } +
+ +@code { + private Dictionary>? _groups; + private DocIndexItem? _selectedValue; + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (!firstRender) + return; + + var items = await Http.GetFromJsonAsync>("/docs/docs-index.json") ?? []; + + _groups = items + .GroupBy(x => x.Group) + .OrderBy(g => g.Min(x => x.GroupOrder)) + .ToDictionary( + g => g.Key, + g => g.OrderBy(x => x.Order).ToList() + ); + + StateHasChanged(); + } + + private string GetItemClass(DocIndexItem item) + { + var current = Nav.ToBaseRelativePath(Nav.Uri); + + return current == $"docs/{item.Slug}" + ? "mud-selected-item" + : ""; + } + + private string FormatGroup(string group) + { + if (string.IsNullOrWhiteSpace(group)) + return "General"; + + return System.Globalization.CultureInfo.CurrentCulture.TextInfo + .ToTitleCase(group.Replace("-", " ")); + } + + private class DocIndexItem + { + public string Title { get; set; } = ""; + public string Slug { get; set; } = ""; + public int Order { get; set; } + public string Group { get; set; } = ""; + public int GroupOrder { get; set; } = 999; + } +} diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Program.cs b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Program.cs index c8dbf753..2b668304 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Program.cs +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Program.cs @@ -7,4 +7,10 @@ builder.Services.AddMudServices(); builder.Services.AddMudExtensions(); +builder.Services.AddScoped(sp => + new HttpClient + { + BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) + }); + await builder.Build().RunAsync(); diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/device-management.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/device-management.json new file mode 100644 index 00000000..1c0cb6ad --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/device-management.json @@ -0,0 +1,5 @@ +{ + "Slug": "auth-flows/device-management", + "Title": "Device Management", + "Html": "\n\u003Cp\u003EIn UltimateAuth, devices are not an afterthought.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 They are a \u003Cstrong\u003Efirst-class concept\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022why-device-matters\u0022\u003E\uD83E\uDDE0 Why Device Matters\u003C/h2\u003E\n\u003Cp\u003EMost authentication systems ignore devices.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EA user logs in\u003C/li\u003E\n\u003Cli\u003EA token is issued\u003C/li\u003E\n\u003Cli\u003EEverything is treated the same\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This breaks down when you need:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EMulti-device control\u003C/li\u003E\n\u003Cli\u003ESession visibility\u003C/li\u003E\n\u003Cli\u003ESecurity enforcement\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 UltimateAuth solves this with \u003Cstrong\u003Edevice-aware authentication\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022core-concept-chain-device\u0022\u003E\uD83E\uDDE9 Core Concept: Chain = Device\u003C/h2\u003E\n\u003Cp\u003EIn UltimateAuth:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 A \u003Cstrong\u003ESessionChain represents a device\u003C/strong\u003E\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003EDevice \u2192 Chain \u2192 Sessions\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EEach chain:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EIs bound to a device\u003C/li\u003E\n\u003Cli\u003EGroups sessions\u003C/li\u003E\n\u003Cli\u003ETracks activity\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 A device is not inferred \u2014 it is explicitly modeled\u003C/p\u003E\n\u003Ch2 id=\u0022what-defines-a-device\u0022\u003E\uD83D\uDD17 What Defines a Device?\u003C/h2\u003E\n\u003Cp\u003EA chain includes:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EDeviceId\u003C/li\u003E\n\u003Cli\u003EPlatform (web, mobile, etc.)\u003C/li\u003E\n\u003Cli\u003EOperating System\u003C/li\u003E\n\u003Cli\u003EBrowser\u003C/li\u003E\n\u003Cli\u003EIP (optional binding)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This forms a \u003Cstrong\u003Edevice fingerprint\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022device-lifecycle\u0022\u003E\uD83D\uDD04 Device Lifecycle\u003C/h2\u003E\n\u003Ch3 id=\u0022first-login\u0022\u003E1\uFE0F\u20E3 First Login\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ENew device detected\u003C/li\u003E\n\u003Cli\u003ENew chain is created\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022subsequent-logins\u0022\u003E2\uFE0F\u20E3 Subsequent Logins\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ESame device \u2192 reuse chain\u003C/li\u003E\n\u003Cli\u003ENew device \u2192 new chain\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Device continuity is preserved\u003C/p\u003E\n\u003Ch3 id=\u0022activity-touch\u0022\u003E3\uFE0F\u20E3 Activity (Touch)\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EChain \u003Ccode\u003ELastSeenAt\u003C/code\u003E updated\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003ETouchCount\u003C/code\u003E increases\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Tracks real usage\u003C/p\u003E\n\u003Ch3 id=\u0022token-rotation\u0022\u003E4\uFE0F\u20E3 Token Rotation\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession changes\u003C/li\u003E\n\u003Cli\u003EChain remains\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003ERotationCount\u003C/code\u003E increases\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Device identity stays stable\u003C/p\u003E\n\u003Ch3 id=\u0022logout\u0022\u003E5\uFE0F\u20E3 Logout\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession removed\u003C/li\u003E\n\u003Cli\u003EChain remains\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Device still trusted\u003C/p\u003E\n\u003Ch3 id=\u0022revoke\u0022\u003E6\uFE0F\u20E3 Revoke\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EChain invalidated\u003C/li\u003E\n\u003Cli\u003EAll sessions removed\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Device trust is reset\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022security-model\u0022\u003E\uD83D\uDD10 Security Model\u003C/h2\u003E\n\u003Ch3 id=\u0022device-binding\u0022\u003E\uD83D\uDD17 Device Binding\u003C/h3\u003E\n\u003Cp\u003ESessions and tokens are tied to:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EChain\u003C/li\u003E\n\u003Cli\u003EDevice context\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Prevents cross-device reuse\u003C/p\u003E\n\u003Ch3 id=\u0022rotation-tracking\u0022\u003E\uD83D\uDD01 Rotation Tracking\u003C/h3\u003E\n\u003Cp\u003EChains track:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERotationCount\u003C/li\u003E\n\u003Cli\u003ETouchCount\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Enables anomaly detection\u003C/p\u003E\n\u003Ch3 id=\u0022revoke-cascade\u0022\u003E\uD83D\uDEA8 Revoke Cascade\u003C/h3\u003E\n\u003Cp\u003EIf a device is compromised:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEntire chain can be revoked\u003C/li\u003E\n\u003Cli\u003EAll sessions invalidated\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Immediate containment\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022configuration\u0022\u003E\u2699\uFE0F Configuration\u003C/h2\u003E\n\u003Cp\u003EDevice behavior is configurable via session options:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EMax chains per user\u003C/li\u003E\n\u003Cli\u003EMax sessions per chain\u003C/li\u003E\n\u003Cli\u003EPlatform-based limits\u003C/li\u003E\n\u003Cli\u003EDevice mismatch behavior\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Fine-grained control for enterprise scenarios\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Device = Chain\u003Cbr /\u003E\n\uD83D\uDC49 Not just metadata\u003C/p\u003E\n\u003Ch2 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EDevices are explicitly modeled\u003C/li\u003E\n\u003Cli\u003EEach device has its own chain\u003C/li\u003E\n\u003Cli\u003ESessions belong to chains\u003C/li\u003E\n\u003Cli\u003ESecurity is enforced per device\u003C/li\u003E\n\u003Cli\u003ELogout and revoke operate on device scope\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003EConfiguration \u0026amp; Extensibility\u003C/strong\u003E\u003C/p\u003E\n" +} \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/index.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/index.json new file mode 100644 index 00000000..15fe6ca3 --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/index.json @@ -0,0 +1,5 @@ +{ + "Slug": "auth-flows/index", + "Title": "Auth Flows", + "Html": "\n\u003Cp\u003EAuthentication in UltimateAuth is not a single operation.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 It is a \u003Cstrong\u003Eflow-driven system\u003C/strong\u003E.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022what-is-an-auth-flow\u0022\u003E\uD83E\uDDE0 What is an Auth Flow?\u003C/h2\u003E\n\u003Cp\u003EAn auth flow represents a complete authentication operation, such as:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogging in\u003C/li\u003E\n\u003Cli\u003ERefreshing a session\u003C/li\u003E\n\u003Cli\u003ELogging out\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EEach flow:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EHas a defined lifecycle\u003C/li\u003E\n\u003Cli\u003ERuns through the orchestration pipeline\u003C/li\u003E\n\u003Cli\u003EProduces a controlled authentication outcome\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Instead of calling isolated APIs, you execute \u003Cstrong\u003Eflows\u003C/strong\u003E.\u003C/p\u003E\n\u003Ch2 id=\u0022why-flow-based\u0022\u003E\uD83D\uDD04 Why Flow-Based?\u003C/h2\u003E\n\u003Cp\u003ETraditional systems treat authentication as:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EA login endpoint\u003C/li\u003E\n\u003Cli\u003EA token generator\u003C/li\u003E\n\u003Cli\u003EA cookie setter\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 These approaches often lead to fragmented logic.\u003C/p\u003E\n\u003Cp\u003EUltimateAuth solves this by:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EStructuring authentication as flows\u003C/li\u003E\n\u003Cli\u003EEnforcing a consistent execution model\u003C/li\u003E\n\u003Cli\u003ECentralizing security decisions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022what-happens-during-a-flow\u0022\u003E\uD83E\uDDE9 What Happens During a Flow?\u003C/h2\u003E\n\u003Cp\u003EEvery flow follows the same pattern:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003EFlow \u2192 Context \u2192 Orchestrator \u2192 Authority \u2192 Result\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cul\u003E\n\u003Cli\u003EThe \u003Cstrong\u003Eflow\u003C/strong\u003E defines the intent\u003C/li\u003E\n\u003Cli\u003EThe \u003Cstrong\u003Econtext\u003C/strong\u003E carries state\u003C/li\u003E\n\u003Cli\u003EThe \u003Cstrong\u003Eorchestrator\u003C/strong\u003E coordinates execution\u003C/li\u003E\n\u003Cli\u003EThe \u003Cstrong\u003Eauthority\u003C/strong\u003E enforces rules\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This ensures consistent and secure behavior across all operations.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022types-of-flows\u0022\u003E\uD83D\uDD10 Types of Flows\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth provides built-in flows for common scenarios:\u003C/p\u003E\n\u003Ch3 id=\u0022login-flow\u0022\u003E\uD83D\uDD11 Login Flow\u003C/h3\u003E\n\u003Cp\u003EEstablishes authentication by:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EValidating credentials\u003C/li\u003E\n\u003Cli\u003ECreating session hierarchy (root, chain, session)\u003C/li\u003E\n\u003Cli\u003EIssuing tokens if required\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ca href=\u0022./login-flow.md\u0022\u003ELearn more\u003C/a\u003E\u003C/p\u003E\n\u003Ch3 id=\u0022refresh-flow\u0022\u003E\uD83D\uDD04 Refresh Flow\u003C/h3\u003E\n\u003Cp\u003EExtends an existing session:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERotates refresh tokens\u003C/li\u003E\n\u003Cli\u003EMaintains session continuity\u003C/li\u003E\n\u003Cli\u003EApplies sliding expiration\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ca href=\u0022./refresh-flow.md\u0022\u003ELearn more\u003C/a\u003E\u003C/p\u003E\n\u003Ch3 id=\u0022logout-flow\u0022\u003E\uD83D\uDEAA Logout Flow\u003C/h3\u003E\n\u003Cp\u003ETerminates authentication:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERevokes session(s)\u003C/li\u003E\n\u003Cli\u003EInvalidates tokens\u003C/li\u003E\n\u003Cli\u003ESupports device-level or global logout\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ca href=\u0022./logout-flow.md\u0022\u003ELearn more\u003C/a\u003E\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022supporting-concepts\u0022\u003E\uD83E\uDDE0 Supporting Concepts\u003C/h2\u003E\n\u003Cp\u003EThese flows operate on top of deeper system models:\u003C/p\u003E\n\u003Ch3 id=\u0022session-lifecycle\u0022\u003E\uD83E\uDDEC Session Lifecycle\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ERoot \u2192 Chain \u2192 Session hierarchy\u003C/li\u003E\n\u003Cli\u003EDevice-aware session structure\u003C/li\u003E\n\u003Cli\u003ELifecycle management and revocation\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ca href=\u0022./session-lifecycle.md\u0022\u003ELearn more\u003C/a\u003E\u003C/p\u003E\n\u003Ch3 id=\u0022token-behavior\u0022\u003E\uD83C\uDF9F Token Behavior\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EAccess token vs refresh token\u003C/li\u003E\n\u003Cli\u003EOpaque vs JWT\u003C/li\u003E\n\u003Cli\u003EMode-dependent behavior\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ca href=\u0022./token-behavior.md\u0022\u003ELearn more\u003C/a\u003E\u003C/p\u003E\n\u003Ch3 id=\u0022device-management\u0022\u003E\uD83D\uDCF1 Device Management\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EDevice binding\u003C/li\u003E\n\u003Cli\u003EMulti-device sessions\u003C/li\u003E\n\u003Cli\u003ESecurity implications\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ca href=\u0022./device-management.md\u0022\u003ELearn more\u003C/a\u003E\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Cstrong\u003EAuthentication is not a single step\u003C/strong\u003E\u003Cbr /\u003E\n\uD83D\uDC49 \u003Cstrong\u003EIt is a controlled flow of state transitions\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EAuthentication is executed as flows\u003C/li\u003E\n\u003Cli\u003EEach flow follows a consistent pipeline\u003C/li\u003E\n\u003Cli\u003ESessions and tokens are created as part of flows\u003C/li\u003E\n\u003Cli\u003ESecurity is enforced centrally\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Ch2 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EStart with the most important flow:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Continue to \u003Cstrong\u003ELogin Flow\u003C/strong\u003E\u003C/p\u003E\n" +} \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/login-flow.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/login-flow.json new file mode 100644 index 00000000..171f28fc --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/login-flow.json @@ -0,0 +1,5 @@ +{ + "Slug": "auth-flows/login-flow", + "Title": "Login", + "Html": "\n\u003Cp\u003EThe login flow in UltimateAuth is not just credential validation.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 It is a \u003Cstrong\u003Econtrolled session establishment process\u003C/strong\u003E.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022what-is-login\u0022\u003E\uD83E\uDDE0 What is Login?\u003C/h2\u003E\n\u003Cp\u003EIn traditional systems:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EValidate credentials\u003C/li\u003E\n\u003Cli\u003EIssue a token\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EIn UltimateAuth:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Login creates a \u003Cstrong\u003Esession hierarchy\u003C/strong\u003E\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003ERoot \u2192 Chain \u2192 Session\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003ELogin does not create a token\u003Cbr /\u003E\n\u2192 It creates a session\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cp\u003ETokens are optional outputs derived from the session.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022step-by-step-execution\u0022\u003E\uD83D\uDD04 Step-by-Step Execution\u003C/h2\u003E\n\u003Cp\u003EThe login flow follows a structured pipeline:\u003C/p\u003E\n\u003Ch3 id=\u0022identifier-resolution\u0022\u003E1\uFE0F\u20E3 Identifier Resolution\u003C/h3\u003E\n\u003Cp\u003EThe system resolves the user identity:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUsername / email / phone \u2192 \u003Ccode\u003EUserKey\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022user-security-state\u0022\u003E2\uFE0F\u20E3 User \u0026amp; Security State\u003C/h3\u003E\n\u003Cp\u003EThe system loads:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUser existence\u003C/li\u003E\n\u003Cli\u003EAccount state\u003C/li\u003E\n\u003Cli\u003EFactor (credential) state\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EChecks include:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EIs the account locked?\u003C/li\u003E\n\u003Cli\u003EIs reauthentication required?\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022credential-validation\u0022\u003E3\uFE0F\u20E3 Credential Validation\u003C/h3\u003E\n\u003Cp\u003ECredentials are validated using providers:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EPassword\u003C/li\u003E\n\u003Cli\u003E(Extensible: OTP, external providers, etc.)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022authority-decision\u0022\u003E4\uFE0F\u20E3 Authority Decision\u003C/h3\u003E\n\u003Cp\u003EThe \u003Cstrong\u003ELoginAuthority\u003C/strong\u003E evaluates the attempt.\u003C/p\u003E\n\u003Cp\u003EPossible outcomes:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u2705 Allow\u003C/li\u003E\n\u003Cli\u003E\u274C Deny\u003C/li\u003E\n\u003Cli\u003E\u26A0\uFE0F Challenge (e.g. MFA)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 No session is created before this decision.\u003C/p\u003E\n\u003Ch3 id=\u0022device-chain-resolution\u0022\u003E5\uFE0F\u20E3 Device \u0026amp; Chain Resolution\u003C/h3\u003E\n\u003Cp\u003EThe system checks if the device is known:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EExisting device \u2192 reuse chain\u003C/li\u003E\n\u003Cli\u003ENew device \u2192 create new chain\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 A \u003Cstrong\u003EChain represents a device\u003C/strong\u003E\u003C/p\u003E\n\u003Ch3 id=\u0022session-creation\u0022\u003E6\uFE0F\u20E3 Session Creation\u003C/h3\u003E\n\u003Cp\u003EA new session is issued:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELinked to user\u003C/li\u003E\n\u003Cli\u003ELinked to device (chain)\u003C/li\u003E\n\u003Cli\u003EBound to tenant\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003ESession hierarchy:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003EUser \u2192 Root \u2192 Chain \u2192 Session\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022token-issuance-optional\u0022\u003E7\uFE0F\u20E3 Token Issuance (Optional)\u003C/h3\u003E\n\u003Cp\u003EDepending on the mode and request:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAccess token may be issued\u003C/li\u003E\n\u003Cli\u003ERefresh token may be issued\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Tokens are derived from the session\u003Cbr /\u003E\n\uD83D\uDC49 Not the source of truth\u003C/p\u003E\n\u003Ch3 id=\u0022event-dispatch\u0022\u003E8\uFE0F\u20E3 Event Dispatch\u003C/h3\u003E\n\u003Cp\u003EThe system emits:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogin events\u003C/li\u003E\n\u003Cli\u003EAudit information\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022what-gets-created\u0022\u003E\uD83E\uDDE9 What Gets Created?\u003C/h2\u003E\n\u003Cp\u003EA successful login creates:\u003C/p\u003E\n\u003Ch3 id=\u0022root\u0022\u003E\uD83D\uDD39 Root\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EOne per user\u003C/li\u003E\n\u003Cli\u003ERepresents global authentication state\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022chain\u0022\u003E\uD83D\uDD39 Chain\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EOne per device\u003C/li\u003E\n\u003Cli\u003EManages device lifecycle\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022session\u0022\u003E\uD83D\uDD39 Session\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EIndividual authentication instance\u003C/li\u003E\n\u003Cli\u003ERepresents a single login\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022device-awareness\u0022\u003E\uD83D\uDCF1 Device Awareness\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth is device-aware by design:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEach device gets its own chain\u003C/li\u003E\n\u003Cli\u003ESessions are grouped by device\u003C/li\u003E\n\u003Cli\u003ELogout can target device or all sessions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022security-considerations\u0022\u003E\uD83D\uDD10 Security Considerations\u003C/h2\u003E\n\u003Cp\u003EThe login flow includes built-in protections:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAccount lockout\u003C/li\u003E\n\u003Cli\u003EFailed attempt tracking\u003C/li\u003E\n\u003Cli\u003EDevice binding\u003C/li\u003E\n\u003Cli\u003ESecurity version validation\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Security decisions are centralized in the Authority\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Login = session creation\u003Cbr /\u003E\n\uD83D\uDC49 Not token issuance\u003C/p\u003E\n\u003Ch2 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogin is a flow, not a function\u003C/li\u003E\n\u003Cli\u003EAuthority decides before any state change\u003C/li\u003E\n\u003Cli\u003ESessions are the source of truth\u003C/li\u003E\n\u003Cli\u003ETokens are optional representations\u003C/li\u003E\n\u003Cli\u003EDevice context is always considered\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003ERefresh Flow\u003C/strong\u003E\u003C/p\u003E\n" +} \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/logout-flow.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/logout-flow.json new file mode 100644 index 00000000..698ea024 --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/logout-flow.json @@ -0,0 +1,5 @@ +{ + "Slug": "auth-flows/logout-flow", + "Title": "Logout", + "Html": "\n\u003Cp\u003EThe logout flow in UltimateAuth is not a single action.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 It represents different \u003Cstrong\u003Elevels of authentication termination\u003C/strong\u003E.\u003C/p\u003E\n\u003Ch2 id=\u0022what-is-logout\u0022\u003E\uD83E\uDDE0 What is Logout?\u003C/h2\u003E\n\u003Cp\u003EIn traditional systems:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogout = remove cookie or token\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EIn UltimateAuth:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Logout affects \u003Cstrong\u003Esession, device, or identity scope\u003C/strong\u003E\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003ELogout is not just removing access\u003Cbr /\u003E\n\u2192 It is controlling session lifecycle\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Ch2 id=\u0022logout-vs-revoke\u0022\u003E\uD83D\uDD00 Logout vs Revoke\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth distinguishes between two concepts:\u003C/p\u003E\n\u003Ch3 id=\u0022logout-soft-termination\u0022\u003E\uD83D\uDD39 Logout (Soft Termination)\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EEnds the current session\u003C/li\u003E\n\u003Cli\u003EKeeps the device (chain) active\u003C/li\u003E\n\u003Cli\u003EAllows re-login without resetting device context\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cpre\u003E\u003Ccode\u003ESession \u2192 Invalidated\nChain \u2192 Still Active\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 User can log in again and continue on the same device chain\u003C/p\u003E\n\u003Ch3 id=\u0022revoke-hard-invalidation\u0022\u003E\uD83D\uDD25 Revoke (Hard Invalidation)\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EInvalidates session, chain, or root\u003C/li\u003E\n\u003Cli\u003ECannot be undone\u003C/li\u003E\n\u003Cli\u003EForces a completely new authentication path\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cpre\u003E\u003Ccode\u003EChain \u2192 Revoked\nSessions \u2192 Revoked\nNext login \u2192 New chain\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Revoke resets trust for that scope\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022levels-of-termination\u0022\u003E\uD83E\uDDE9 Levels of Termination\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth supports multiple logout scopes:\u003C/p\u003E\n\u003Ch3 id=\u0022session-level-logout\u0022\u003E\uD83D\uDD39 Session-Level Logout\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ETerminates a single session\u003C/li\u003E\n\u003Cli\u003EOther sessions on the same device may remain\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022device-level-chain\u0022\u003E\uD83D\uDCF1 Device-Level (Chain)\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ETerminates all sessions on a device\u003C/li\u003E\n\u003Cli\u003EDevice chain is invalidated or reset\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022global-logout-all-devices\u0022\u003E\uD83C\uDF10 Global Logout (All Devices)\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ETerminates all sessions across all devices\u003C/li\u003E\n\u003Cli\u003EKeeps root (user identity) intact\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022root-revoke\u0022\u003E\uD83D\uDD25 Root Revoke\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EInvalidates entire authentication state\u003C/li\u003E\n\u003Cli\u003EAll chains and sessions are revoked\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This is the strongest possible action\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022step-by-step-execution\u0022\u003E\uD83D\uDD04 Step-by-Step Execution\u003C/h2\u003E\n\u003Ch3 id=\u0022flow-context-resolution\u0022\u003E1\uFE0F\u20E3 Flow Context Resolution\u003C/h3\u003E\n\u003Cp\u003EThe system resolves:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECurrent session\u003C/li\u003E\n\u003Cli\u003EUser identity\u003C/li\u003E\n\u003Cli\u003ETenant\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022authority-decision\u0022\u003E2\uFE0F\u20E3 Authority Decision\u003C/h3\u003E\n\u003Cp\u003ELogout operations are validated:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAuthorization checks\u003C/li\u003E\n\u003Cli\u003EAccess validation\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Logout is not blindly executed\u003C/p\u003E\n\u003Ch3 id=\u0022scope-determination\u0022\u003E3\uFE0F\u20E3 Scope Determination\u003C/h3\u003E\n\u003Cp\u003EThe system determines what to terminate:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession\u003C/li\u003E\n\u003Cli\u003EChain\u003C/li\u003E\n\u003Cli\u003ERoot\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022execution\u0022\u003E4\uFE0F\u20E3 Execution\u003C/h3\u003E\n\u003Cp\u003EDepending on scope:\u003C/p\u003E\n\u003Ch4 id=\u0022session-logout\u0022\u003ESession Logout\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession is revoked\u003C/li\u003E\n\u003Cli\u003EOther sessions unaffected\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch4 id=\u0022chain-revoke-logout\u0022\u003EChain Revoke / Logout\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003EAll sessions in the chain are revoked\u003C/li\u003E\n\u003Cli\u003EDevice trust is reset\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch4 id=\u0022global-logout\u0022\u003EGlobal Logout\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003EAll chains are revoked (optionally excluding current)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch4 id=\u0022root-revoke-1\u0022\u003ERoot Revoke\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003EEntire identity state is invalidated\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022event-dispatch\u0022\u003E5\uFE0F\u20E3 Event Dispatch\u003C/h3\u003E\n\u003Cp\u003EThe system emits:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogout events\u003C/li\u003E\n\u003Cli\u003EAudit logs\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022device-awareness\u0022\u003E\uD83D\uDCF1 Device Awareness\u003C/h2\u003E\n\u003Cp\u003ELogout behavior is device-aware:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEach device is a chain\u003C/li\u003E\n\u003Cli\u003ELogout can target specific devices\u003C/li\u003E\n\u003Cli\u003ESessions are grouped by device\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This enables fine-grained control\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022security-model\u0022\u003E\uD83D\uDD10 Security Model\u003C/h2\u003E\n\u003Ch3 id=\u0022controlled-termination\u0022\u003E\uD83D\uDD12 Controlled Termination\u003C/h3\u003E\n\u003Cp\u003EAll logout operations:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EPass through orchestrator\u003C/li\u003E\n\u003Cli\u003EAre validated by authority\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Prevents unauthorized session manipulation\u003C/p\u003E\n\u003Ch3 id=\u0022irreversible-revocation\u0022\u003E\uD83D\uDD01 Irreversible Revocation\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ERevoked chains cannot be restored\u003C/li\u003E\n\u003Cli\u003ERevoked sessions remain invalid\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Ensures strong security guarantees\u003C/p\u003E\n\u003Ch3 id=\u0022identity-boundaries\u0022\u003E\uD83D\uDD17 Identity Boundaries\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession \u2192 temporary identity proof\u003C/li\u003E\n\u003Cli\u003EChain \u2192 device trust boundary\u003C/li\u003E\n\u003Cli\u003ERoot \u2192 global identity state\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Logout operates within these boundaries\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Logout = ending a session\u003Cbr /\u003E\n\uD83D\uDC49 Revoke = resetting trust\u003C/p\u003E\n\u003Ch2 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogout and revoke are different operations\u003C/li\u003E\n\u003Cli\u003ELogout is reversible (via re-login)\u003C/li\u003E\n\u003Cli\u003ERevoke is permanent and forces new authentication\u003C/li\u003E\n\u003Cli\u003EDevice (chain) is a first-class concept\u003C/li\u003E\n\u003Cli\u003ESecurity is enforced through authority and orchestrator\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003ESession Lifecycle\u003C/strong\u003E\u003C/p\u003E\n" +} \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/refresh-flow.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/refresh-flow.json new file mode 100644 index 00000000..b204325f --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/refresh-flow.json @@ -0,0 +1,5 @@ +{ + "Slug": "auth-flows/refresh-flow", + "Title": "Refresh", + "Html": "\n\u003Cp\u003EThe refresh flow in UltimateAuth is not a single fixed operation.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 It is a \u003Cstrong\u003Emode-dependent continuation strategy\u003C/strong\u003E.\u003C/p\u003E\n\u003Ch2 id=\u0022what-is-refresh\u0022\u003E\uD83E\uDDE0 What is Refresh?\u003C/h2\u003E\n\u003Cp\u003EIn traditional systems:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERefresh = get a new access token\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EIn UltimateAuth:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Refresh continues an existing authentication state\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003ERefresh is not re-authentication\u003Cbr /\u003E\n\u2192 It is session continuation\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Ch2 id=\u0022two-refresh-strategies\u0022\u003E\uD83D\uDD00 Two Refresh Strategies\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth supports two fundamentally different refresh behaviors:\u003C/p\u003E\n\u003Ch3 id=\u0022session-touch-stateful\u0022\u003E\uD83D\uDD39 Session Touch (Stateful)\u003C/h3\u003E\n\u003Cp\u003EUsed in \u003Cstrong\u003EPureOpaque mode\u003C/strong\u003E\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ENo tokens involved\u003C/li\u003E\n\u003Cli\u003ENo new session created\u003C/li\u003E\n\u003Cli\u003EExisting session is extended\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cpre\u003E\u003Ccode\u003ESession \u2192 Validate \u2192 Touch \u2192 Continue\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This updates activity without changing identity\u003C/p\u003E\n\u003Ch3 id=\u0022token-rotation-stateless-hybrid\u0022\u003E\uD83D\uDD39 Token Rotation (Stateless / Hybrid)\u003C/h3\u003E\n\u003Cp\u003EUsed in:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Cp\u003EHybrid\u003C/p\u003E\n\u003C/li\u003E\n\u003Cli\u003E\u003Cp\u003ESemiHybrid\u003C/p\u003E\n\u003C/li\u003E\n\u003Cli\u003E\u003Cp\u003EPureJwt\u003C/p\u003E\n\u003C/li\u003E\n\u003Cli\u003E\u003Cp\u003ERefresh token is validated\u003C/p\u003E\n\u003C/li\u003E\n\u003Cli\u003E\u003Cp\u003EOld token is revoked\u003C/p\u003E\n\u003C/li\u003E\n\u003Cli\u003E\u003Cp\u003ENew tokens are issued\u003C/p\u003E\n\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cpre\u003E\u003Ccode\u003ERefreshToken \u2192 Validate \u2192 Revoke \u2192 Issue New Tokens\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This ensures forward security\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mode-based-behavior\u0022\u003E\u2696\uFE0F Mode-Based Behavior\u003C/h2\u003E\n\u003Cp\u003ERefresh behavior is determined by the authentication mode:\u003C/p\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EMode\u003C/th\u003E\n\u003Cth\u003EBehavior\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EPureOpaque\u003C/td\u003E\n\u003Ctd\u003ESession Touch\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EHybrid\u003C/td\u003E\n\u003Ctd\u003ERotation \u002B Touch\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ESemiHybrid\u003C/td\u003E\n\u003Ctd\u003ERotation\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EPureJwt\u003C/td\u003E\n\u003Ctd\u003ERotation\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Cp\u003E\uD83D\uDC49 UltimateAuth automatically selects the correct strategy.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022step-by-step-execution\u0022\u003E\uD83D\uDD04 Step-by-Step Execution\u003C/h2\u003E\n\u003Ch3 id=\u0022input-resolution\u0022\u003E1\uFE0F\u20E3 Input Resolution\u003C/h3\u003E\n\u003Cp\u003EThe system resolves:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESessionId (if present)\u003C/li\u003E\n\u003Cli\u003ERefreshToken (if present)\u003C/li\u003E\n\u003Cli\u003EDevice context\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022mode-based-branching\u0022\u003E2\uFE0F\u20E3 Mode-Based Branching\u003C/h3\u003E\n\u003Cp\u003EThe system determines the refresh strategy:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession-based \u2192 Touch\u003C/li\u003E\n\u003Cli\u003EToken-based \u2192 Rotation\u003C/li\u003E\n\u003Cli\u003EHybrid \u2192 Both\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022session-validation\u0022\u003E3\uFE0F\u20E3 Session Validation\u003C/h3\u003E\n\u003Cp\u003EIf session is involved:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession is validated\u003C/li\u003E\n\u003Cli\u003EDevice binding is checked\u003C/li\u003E\n\u003Cli\u003EExpiration is evaluated\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022token-validation-if-applicable\u0022\u003E4\uFE0F\u20E3 Token Validation (if applicable)\u003C/h3\u003E\n\u003Cp\u003EIf refresh token is used:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EToken is validated\u003C/li\u003E\n\u003Cli\u003ESession and chain are verified\u003C/li\u003E\n\u003Cli\u003EDevice consistency is checked\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022security-checks\u0022\u003E5\uFE0F\u20E3 Security Checks\u003C/h3\u003E\n\u003Cp\u003EThe system enforces:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EToken reuse detection\u003C/li\u003E\n\u003Cli\u003ESession validity\u003C/li\u003E\n\u003Cli\u003EChain integrity\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 If validation fails \u2192 reauthentication required\u003C/p\u003E\n\u003Ch3 id=\u0022execution\u0022\u003E6\uFE0F\u20E3 Execution\u003C/h3\u003E\n\u003Cp\u003EDepending on the strategy:\u003C/p\u003E\n\u003Ch4 id=\u0022session-touch\u0022\u003ESession Touch\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003EUpdates \u003Ccode\u003ELastSeenAt\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003EApplies sliding expiration\u003C/li\u003E\n\u003Cli\u003ENo new tokens issued\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch4 id=\u0022token-rotation\u0022\u003EToken Rotation\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003ERevokes old refresh token\u003C/li\u003E\n\u003Cli\u003EIssues new access token\u003C/li\u003E\n\u003Cli\u003EIssues new refresh token\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch4 id=\u0022hybrid-mode\u0022\u003EHybrid Mode\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003EValidates session\u003C/li\u003E\n\u003Cli\u003ERotates tokens\u003C/li\u003E\n\u003Cli\u003EUpdates session activity\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022response-generation\u0022\u003E7\uFE0F\u20E3 Response Generation\u003C/h3\u003E\n\u003Cp\u003EThe response may include:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESessionId\u003C/li\u003E\n\u003Cli\u003EAccess token\u003C/li\u003E\n\u003Cli\u003ERefresh token\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Output depends on mode and client\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022security-model\u0022\u003E\uD83D\uDD10 Security Model\u003C/h2\u003E\n\u003Cp\u003EThe refresh flow includes strong protections:\u003C/p\u003E\n\u003Ch3 id=\u0022token-reuse-detection\u0022\u003E\uD83D\uDD01 Token Reuse Detection\u003C/h3\u003E\n\u003Cp\u003EIf a refresh token is reused:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EChain may be revoked\u003C/li\u003E\n\u003Cli\u003ESession may be revoked\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This prevents replay attacks\u003C/p\u003E\n\u003Ch3 id=\u0022session-binding\u0022\u003E\uD83D\uDD17 Session Binding\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ETokens are bound to session\u003C/li\u003E\n\u003Cli\u003ESession is bound to device\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Prevents token misuse across devices\u003C/p\u003E\n\u003Ch3 id=\u0022chain-integrity\u0022\u003E\uD83E\uDDEC Chain Integrity\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ERefresh operates within a chain\u003C/li\u003E\n\u003Cli\u003ECross-device usage is rejected\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Refresh = continuation\u003Cbr /\u003E\n\uD83D\uDC49 Not new authentication\u003C/p\u003E\n\u003Ch2 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003ERefresh behavior depends on auth mode\u003C/li\u003E\n\u003Cli\u003EStateful systems use session touch\u003C/li\u003E\n\u003Cli\u003EStateless systems use token rotation\u003C/li\u003E\n\u003Cli\u003EHybrid systems combine both\u003C/li\u003E\n\u003Cli\u003ESecurity is enforced at every step\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003ELogout Flow\u003C/strong\u003E\u003C/p\u003E\n" +} \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/session-lifecycle.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/session-lifecycle.json new file mode 100644 index 00000000..909bb101 --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/session-lifecycle.json @@ -0,0 +1,5 @@ +{ + "Slug": "auth-flows/session-lifecycle", + "Title": "Session Lifecycle", + "Html": "\n\u003Cp\u003EUltimateAuth is built around a structured session model.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Authentication is not a token\u003Cbr /\u003E\n\uD83D\uDC49 It is a \u003Cstrong\u003Ehierarchical session system\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022core-model\u0022\u003E\uD83E\uDDE0 Core Model\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth defines three core entities:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003ERoot \u2192 Chain \u2192 Session\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022root-identity-authority\u0022\u003E\uD83D\uDD39 Root (Identity Authority)\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EOne per user\u003C/li\u003E\n\u003Cli\u003ERepresents global authentication state\u003C/li\u003E\n\u003Cli\u003EHolds security version\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Root defines \u003Cstrong\u003Ewho the user is\u003C/strong\u003E\u003C/p\u003E\n\u003Ch3 id=\u0022chain-device-context\u0022\u003E\uD83D\uDCF1 Chain (Device Context)\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EOne per device\u003C/li\u003E\n\u003Cli\u003ERepresents a device-bound identity context\u003C/li\u003E\n\u003Cli\u003ETracks activity (LastSeenAt)\u003C/li\u003E\n\u003Cli\u003EManages session rotation and touch\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 A chain is a \u003Cstrong\u003Etrusted device boundary\u003C/strong\u003E\u003C/p\u003E\n\u003Ch3 id=\u0022session-authentication-instance\u0022\u003E\uD83D\uDD11 Session (Authentication Instance)\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ECreated on login\u003C/li\u003E\n\u003Cli\u003ERepresents a single authentication event\u003C/li\u003E\n\u003Cli\u003EHas expiration and revocation state\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 A session is a \u003Cstrong\u003Eproof of authentication\u003C/strong\u003E\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022relationship\u0022\u003E\uD83D\uDD17 Relationship\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode\u003EUser\n\u2514\u2500\u2500 Root\n\u2514\u2500\u2500 Chain (Device)\n\u2514\u2500\u2500 Session (Instance)\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Each level adds more specificity:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERoot \u2192 identity\u003C/li\u003E\n\u003Cli\u003EChain \u2192 device\u003C/li\u003E\n\u003Cli\u003ESession \u2192 login instance\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022lifecycle-overview\u0022\u003E\uD83D\uDD04 Lifecycle Overview\u003C/h2\u003E\n\u003Ch3 id=\u0022creation-login\u0022\u003E1\uFE0F\u20E3 Creation (Login)\u003C/h3\u003E\n\u003Cp\u003EWhen a user logs in:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERoot is created (if not exists)\u003C/li\u003E\n\u003Cli\u003EChain is resolved or created\u003C/li\u003E\n\u003Cli\u003ESession is issued\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022active-usage\u0022\u003E2\uFE0F\u20E3 Active Usage\u003C/h3\u003E\n\u003Cp\u003EDuring normal operation:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession is validated\u003C/li\u003E\n\u003Cli\u003EChain \u003Ccode\u003ELastSeenAt\u003C/code\u003E is updated (touch)\u003C/li\u003E\n\u003Cli\u003ESliding expiration may apply\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Activity updates the \u003Cstrong\u003Echain\u003C/strong\u003E, not just the session\u003C/p\u003E\n\u003Ch3 id=\u0022refresh\u0022\u003E3\uFE0F\u20E3 Refresh\u003C/h3\u003E\n\u003Cp\u003EDepending on mode:\u003C/p\u003E\n\u003Ch4 id=\u0022session-based-pureopaque\u0022\u003ESession-Based (PureOpaque)\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession remains\u003C/li\u003E\n\u003Cli\u003EChain is touched\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch4 id=\u0022token-based-hybrid-jwt\u0022\u003EToken-Based (Hybrid / JWT)\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession continues\u003C/li\u003E\n\u003Cli\u003ETokens are rotated\u003C/li\u003E\n\u003Cli\u003EChain rotation count increases\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Chain tracks behavior:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERotationCount\u003C/li\u003E\n\u003Cli\u003ETouchCount\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022expiration\u0022\u003E4\uFE0F\u20E3 Expiration\u003C/h3\u003E\n\u003Cp\u003EA session may expire due to:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELifetime expiration\u003C/li\u003E\n\u003Cli\u003EIdle timeout\u003C/li\u003E\n\u003Cli\u003EAbsolute expiration\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Expired \u2260 revoked\u003C/p\u003E\n\u003Ch3 id=\u0022revocation\u0022\u003E5\uFE0F\u20E3 Revocation\u003C/h3\u003E\n\u003Cp\u003ERevocation can occur at multiple levels:\u003C/p\u003E\n\u003Ch4 id=\u0022session-revocation\u0022\u003ESession Revocation\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003ESingle session invalidated\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch4 id=\u0022chain-revocation\u0022\u003EChain Revocation\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003EAll sessions on device invalidated\u003C/li\u003E\n\u003Cli\u003EDevice trust reset\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Ch4 id=\u0022root-revocation\u0022\u003ERoot Revocation\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003EAll chains and sessions invalidated\u003C/li\u003E\n\u003Cli\u003ESecurity version increased\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Revocation is irreversible\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022security-model\u0022\u003E\uD83D\uDD10 Security Model\u003C/h2\u003E\n\u003Ch3 id=\u0022security-versioning\u0022\u003E\uD83D\uDD12 Security Versioning\u003C/h3\u003E\n\u003Cp\u003EEach root has:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003ESecurityVersion\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EEach session stores:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003ESecurityVersionAtCreation\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Cp\u003E\uD83D\uDC49 If mismatch:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ESession becomes invalid\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022device-binding\u0022\u003E\uD83D\uDD17 Device Binding\u003C/h3\u003E\n\u003Cp\u003EEach chain is tied to:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EDeviceId\u003C/li\u003E\n\u003Cli\u003EPlatform\u003C/li\u003E\n\u003Cli\u003EOS\u003C/li\u003E\n\u003Cli\u003EBrowser\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Prevents cross-device misuse\u003C/p\u003E\n\u003Ch3 id=\u0022rotation-tracking\u0022\u003E\uD83D\uDD01 Rotation Tracking\u003C/h3\u003E\n\u003Cp\u003EChains track:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERotationCount\u003C/li\u003E\n\u003Cli\u003ETouchCount\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Enables:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Ereplay detection\u003C/li\u003E\n\u003Cli\u003Eanomaly tracking\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022lifecycle-configuration\u0022\u003E\u2699\uFE0F Lifecycle Configuration\u003C/h3\u003E\n\u003Cp\u003ESession behavior is configurable:\u003C/p\u003E\n\u003Cp\u003E\u23F1 Lifetime\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EDefault session duration\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDD04 Sliding Expiration\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EExtends session on activity\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDCA4 Idle Timeout\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EInvalidates inactive sessions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDCF1 Device Limits\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EMax chains per user\u003C/li\u003E\n\u003Cli\u003EMax sessions per chain\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 These are defined via UAuthSessionOptions\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Authentication is a living structure\n\uD83D\uDC49 Not a static token\u003C/p\u003E\n\u003Ch2 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003ESessions are part of a hierarchy\u003C/li\u003E\n\u003Cli\u003EDevice (chain) is a first-class concept\u003C/li\u003E\n\u003Cli\u003ERoot controls global security\u003C/li\u003E\n\u003Cli\u003ESessions are short-lived proofs\u003C/li\u003E\n\u003Cli\u003EChains manage lifecycle and activity\u003C/li\u003E\n\u003Cli\u003ERevocation operates at multiple levels\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to Token Behavior\u003C/p\u003E\n" +} \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/token-behavior.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/token-behavior.json new file mode 100644 index 00000000..957f857b --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/token-behavior.json @@ -0,0 +1,5 @@ +{ + "Slug": "auth-flows/token-behavior", + "Title": "Token Behavior", + "Html": "\n\u003Cp\u003EIn UltimateAuth, tokens are not the foundation of authentication.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 They are \u003Cstrong\u003Ederived artifacts of a session\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022rethinking-tokens\u0022\u003E\uD83E\uDDE0 Rethinking Tokens\u003C/h2\u003E\n\u003Cp\u003EIn traditional systems:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EToken = identity\u003C/li\u003E\n\u003Cli\u003EToken = authentication\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EIn UltimateAuth:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Session = identity\u003Cbr /\u003E\n\uD83D\uDC49 Token = transport mechanism\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003ETokens do not define identity\u003Cbr /\u003E\n\u2192 Sessions do\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Ch2 id=\u0022token-types\u0022\u003E\uD83E\uDDE9 Token Types\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth supports two main token types:\u003C/p\u003E\n\u003Ch3 id=\u0022opaque-tokens\u0022\u003E\uD83D\uDD39 Opaque Tokens\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ERandom, non-decodable values\u003C/li\u003E\n\u003Cli\u003EStored and validated on the server\u003C/li\u003E\n\u003Cli\u003ETypically reference a session\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Used in:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EPureOpaque\u003C/li\u003E\n\u003Cli\u003EHybrid\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022jwt-json-web-tokens\u0022\u003E\uD83D\uDD39 JWT (JSON Web Tokens)\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ESelf-contained tokens\u003C/li\u003E\n\u003Cli\u003EInclude claims and metadata\u003C/li\u003E\n\u003Cli\u003ESigned and verifiable without server lookup\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Used in:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESemiHybrid\u003C/li\u003E\n\u003Cli\u003EPureJwt\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mode-based-behavior\u0022\u003E\u2696\uFE0F Mode-Based Behavior\u003C/h2\u003E\n\u003Cp\u003EToken behavior depends on the authentication mode:\u003C/p\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EMode\u003C/th\u003E\n\u003Cth\u003EAccess Token\u003C/th\u003E\n\u003Cth\u003ERefresh Token\u003C/th\u003E\n\u003Cth\u003EBehavior\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EPureOpaque\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003ESession-only\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EHybrid\u003C/td\u003E\n\u003Ctd\u003E\u2714 (opaque/JWT)\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003Ctd\u003ESession \u002B token\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ESemiHybrid\u003C/td\u003E\n\u003Ctd\u003E\u2714 (JWT)\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003Ctd\u003EJWT \u002B session metadata\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EPureJwt\u003C/td\u003E\n\u003Ctd\u003E\u2714 (JWT)\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003Ctd\u003EFully stateless\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Cp\u003E\uD83D\uDC49 UltimateAuth selects behavior automatically\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022access-tokens\u0022\u003E\uD83D\uDD11 Access Tokens\u003C/h2\u003E\n\u003Cp\u003EAccess tokens represent:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 A \u003Cstrong\u003Etemporary access grant\u003C/strong\u003E\u003C/p\u003E\n\u003Ch3 id=\u0022characteristics\u0022\u003ECharacteristics\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EShort-lived\u003C/li\u003E\n\u003Cli\u003EMode-dependent format\u003C/li\u003E\n\u003Cli\u003EMay contain session reference (\u003Ccode\u003Esid\u003C/code\u003E)\u003C/li\u003E\n\u003Cli\u003EMay include claims\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022important\u0022\u003EImportant\u003C/h3\u003E\n\u003Cp\u003EAccess token is NOT the source of truth.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 It reflects session state, not replaces it\u003C/p\u003E\n\u003Ch2 id=\u0022refresh-tokens\u0022\u003E\uD83D\uDD04 Refresh Tokens\u003C/h2\u003E\n\u003Cp\u003ERefresh tokens represent:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 A \u003Cstrong\u003Econtinuation mechanism\u003C/strong\u003E\u003C/p\u003E\n\u003Ch3 id=\u0022characteristics-1\u0022\u003ECharacteristics\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ELong-lived\u003C/li\u003E\n\u003Cli\u003EStored as hashed values\u003C/li\u003E\n\u003Cli\u003EBound to session and optionally chain\u003C/li\u003E\n\u003Cli\u003ERotated on use\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022lifecycle\u0022\u003ELifecycle\u003C/h3\u003E\n\u003Cp\u003EIssued \u2192 Used \u2192 Replaced \u2192 Revoked\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Old tokens are invalidated on rotation\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022security-model\u0022\u003E\uD83D\uDD10 Security Model\u003C/h2\u003E\n\u003Ch3 id=\u0022rotation\u0022\u003E\uD83D\uDD01 Rotation\u003C/h3\u003E\n\u003Cp\u003EEach refresh:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EInvalidates previous token\u003C/li\u003E\n\u003Cli\u003EIssues a new token\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Prevents replay attacks\u003C/p\u003E\n\u003Ch3 id=\u0022reuse-detection\u0022\u003E\uD83D\uDEA8 Reuse Detection\u003C/h3\u003E\n\u003Cp\u003EIf a token is reused:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EChain may be revoked\u003C/li\u003E\n\u003Cli\u003ESession may be revoked\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Strong forward security\u003C/p\u003E\n\u003Ch3 id=\u0022session-binding\u0022\u003E\uD83D\uDD17 Session Binding\u003C/h3\u003E\n\u003Cp\u003ERefresh tokens include:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESessionId\u003C/li\u003E\n\u003Cli\u003EChainId (optional)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Prevents cross-context usage\u003C/p\u003E\n\u003Ch3 id=\u0022hashed-storage\u0022\u003E\uD83D\uDD12 Hashed Storage\u003C/h3\u003E\n\u003Cp\u003ETokens are:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ENever stored as plaintext\u003C/li\u003E\n\u003Cli\u003EHashed using secure algorithms\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Reduces breach impact\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022token-issuance\u0022\u003E\uD83D\uDD04 Token Issuance\u003C/h2\u003E\n\u003Cp\u003ETokens are issued during:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogin\u003C/li\u003E\n\u003Cli\u003ERefresh\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022access-token\u0022\u003EAccess Token\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EMay be opaque or JWT\u003C/li\u003E\n\u003Cli\u003EIncludes identity and optional session reference\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022refresh-token\u0022\u003ERefresh Token\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EAlways opaque\u003C/li\u003E\n\u003Cli\u003EPersisted in secure store\u003C/li\u003E\n\u003Cli\u003EUsed only for rotation\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Tokens are not identity\u003Cbr /\u003E\n\uD83D\uDC49 They are projections of a session\u003C/p\u003E\n\u003Ch2 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession is the source of truth\u003C/li\u003E\n\u003Cli\u003ETokens are optional and mode-dependent\u003C/li\u003E\n\u003Cli\u003EOpaque tokens require server validation\u003C/li\u003E\n\u003Cli\u003EJWT tokens allow stateless access\u003C/li\u003E\n\u003Cli\u003ERefresh tokens enable secure continuation\u003C/li\u003E\n\u003Cli\u003EToken rotation ensures forward security\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to Device Management\u003C/p\u003E\n" +} \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/authentication.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/authentication.json new file mode 100644 index 00000000..a6c35b2b --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/authentication.json @@ -0,0 +1,5 @@ +{ + "Slug": "client/authentication", + "Title": "Authentication", + "Html": "\n\u003Cp\u003EThis section explains how to use the UltimateAuth client for authentication flows.\u003C/p\u003E\n\u003Ch2 id=\u0022overview\u0022\u003E\uD83E\uDDE0 Overview\u003C/h2\u003E\n\u003Cp\u003EAuthentication in UltimateAuth is \u003Cstrong\u003Eflow-based\u003C/strong\u003E, not endpoint-based.\u003C/p\u003E\n\u003Cp\u003EYou interact with:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ccode\u003EFlowClient\u003C/code\u003E\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch2 id=\u0022login\u0022\u003E\uD83D\uDD11 Login\u003C/h2\u003E\n\u003Ch3 id=\u0022basic-login\u0022\u003EBasic Login\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Flows.LoginAsync(new LoginRequest\n{\n Identifier = \u0026quot;user@ultimateauth.com\u0026quot;,\n Secret = \u0026quot;password\u0026quot;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This triggers a full login flow:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESends credentials\u003C/li\u003E\n\u003Cli\u003EHandles redirect\u003C/li\u003E\n\u003Cli\u003EEstablishes session\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Ch2 id=\u0022try-login-pre-check\u0022\u003E\u26A1 Try Login (Pre-check)\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Evar result = await UAuthClient.Flows.TryLoginAsync(\n new LoginRequest\n {\n Identifier = \u0026quot;user@mail.com\u0026quot;,\n Secret = \u0026quot;password\u0026quot;\n },\n UAuthSubmitMode.TryOnly\n);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022modes\u0022\u003EModes\u003C/h3\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EMode\u003C/th\u003E\n\u003Cth\u003EBehavior\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ETryOnly\u003C/td\u003E\n\u003Ctd\u003EValidate only\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EDirectCommit\u003C/td\u003E\n\u003Ctd\u003ESkip validation\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ETryAndCommit\u003C/td\u003E\n\u003Ctd\u003EValidate then login if success\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Cp\u003E\uD83D\uDC49 Use \u003Ccode\u003EDirectCommit\u003C/code\u003E when:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EYou need maximum performance while sacrificing interactive SPA capabilities.\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Use \u003Ccode\u003ETryOnly\u003C/code\u003E when:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EYou need validation feedback\u003C/li\u003E\n\u003Cli\u003EYou want custom UI flows\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Use \u003Ccode\u003ETryAndCommit\u003C/code\u003E when:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EYou need completely interactive SPA experience.\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ccode\u003ETryAndCommit\u003C/code\u003E is the recommended mode for most applications.\u003C/p\u003E\n\u003Cp\u003EIt provides:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EValidation feedback\u003C/li\u003E\n\u003Cli\u003EAutomatic redirect on success\u003C/li\u003E\n\u003Cli\u003ESmooth SPA experience\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022refresh\u0022\u003E\uD83D\uDD04 Refresh\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Evar result = await UAuthClient.Flows.RefreshAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022possible-outcomes\u0022\u003EPossible Outcomes\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ESuccess \u2192 new tokens/session updated\u003C/li\u003E\n\u003Cli\u003ETouched \u2192 session extended\u003C/li\u003E\n\u003Cli\u003ERotated \u2192 refresh token rotated\u003C/li\u003E\n\u003Cli\u003ENoOp \u2192 nothing changed\u003C/li\u003E\n\u003Cli\u003EReauthRequired \u2192 login required\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Refresh behavior depends on auth mode:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EPureOpaque \u2192 session touch\u003C/li\u003E\n\u003Cli\u003EHybrid/JWT \u2192 token rotation\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EIn default, successful refresh returns success outcome. If you want to learn success detail such as no-op, touched or rotated, open it via server options:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthServer(o =\u0026gt;\n{\n o.Diagnostics.EnableRefreshDetails = true;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022logout\u0022\u003E\uD83D\uDEAA Logout\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Flows.LogoutAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEnds current session\u003C/li\u003E\n\u003Cli\u003EClears authentication state\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022device-logout-variants\u0022\u003E\uD83D\uDCF1 Device Logout Variants\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Flows.LogoutMyDeviceAsync(sessionId);\nawait UAuthClient.Flows.LogoutMyOtherDevicesAsync();\nawait UAuthClient.Flows.LogoutAllMyDevicesAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 These operate on \u003Cstrong\u003Esession chains (devices)\u003C/strong\u003E\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022pkce-flow-public-clients\u0022\u003E\uD83D\uDD10 PKCE Flow (Public Clients)\u003C/h2\u003E\n\u003Ch3 id=\u0022start-pkce\u0022\u003EStart PKCE\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Flows.BeginPkceAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022complete-pkce\u0022\u003EComplete PKCE\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Flows.CompletePkceLoginAsync(request);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003EComplete PKCE also has try semantics the same as login flow. UltimateAuth suggests to use \u003Ccode\u003ETryCompletePkceLoginAsync\u003C/code\u003E for complete interactive experience.\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cp\u003E\uD83D\uDC49 Required for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESPA\u003C/li\u003E\n\u003Cli\u003EBlazor WASM\u003C/li\u003E\n\u003Cli\u003EMobile apps\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Ch2 id=\u0022security-note\u0022\u003E\uD83D\uDEA8 Security Note\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EPublic clients MUST use PKCE\u003C/li\u003E\n\u003Cli\u003EServer clients MAY allow direct login\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EDirect credential posting disabled by default and throws exception when you directly call login. You can enable it via options. You should only use it for debugging and development purposes.\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthClientBlazor(o =\u0026gt;\n{\n o.Login.AllowCredentialPost = true;\n});\n---\n\n## \uD83C\uDFAF Summary\n\nAuthentication in UltimateAuth:\n\n- is flow-driven\n- adapts to client type\n- enforces security by design\n\n---\n\n\uD83D\uDC49 Always use \u0060FlowClient\u0060 for authentication operations\n\u003C/code\u003E\u003C/pre\u003E\n" +} \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/authorization.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/authorization.json new file mode 100644 index 00000000..d8dc663e --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/authorization.json @@ -0,0 +1,5 @@ +{ + "Slug": "client/authorization", + "Title": "Authorization", + "Html": "\n\u003Cp\u003EThis section explains how to manage roles, permissions, and access control using the UltimateAuth client.\u003C/p\u003E\n\u003Ch2 id=\u0022overview\u0022\u003E\uD83E\uDDE0 Overview\u003C/h2\u003E\n\u003Cp\u003EAuthorization in UltimateAuth is policy-driven and role-based.\u003C/p\u003E\n\u003Cp\u003EOn the client, you interact with:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003EUAuthClient.Authorization\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022core-concepts\u0022\u003E\uD83D\uDD11 Core Concepts\u003C/h2\u003E\n\u003Cp\u003ERoles\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ENamed groups of permissions\u003C/li\u003E\n\u003Cli\u003EAssigned to users\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EPermissions\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EFine-grained access definitions\u003C/li\u003E\n\u003Cli\u003EExample: users.create.anonymous, users.delete.self, authorization.roles.admin\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EPolicies\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERuntime decision rules\u003C/li\u003E\n\u003Cli\u003EEnforced automatically on server\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch3 id=\u0022query-roles\u0022\u003E\uD83D\uDCCB Query Roles\u003C/h3\u003E\n\u003Cp\u003Evar result = await UAuthClient.Authorization.QueryRolesAsync(new RoleQuery\n);\u003C/p\u003E\n\u003Ch3 id=\u0022create-role\u0022\u003E\u2795 Create Role\u003C/h3\u003E\n\u003Cp\u003Eawait UAuthClient.Authorization.CreateRoleAsync(new CreateRoleRequest\n);\u003C/p\u003E\n\u003Ch3 id=\u0022rename-role\u0022\u003E\u270F\uFE0F Rename Role\u003C/h3\u003E\n\u003Cp\u003Eawait UAuthClient.Authorization.RenameRoleAsync(new RenameRoleRequest\n);\u003C/p\u003E\n\u003Ch3 id=\u0022set-permissions\u0022\u003E\uD83E\uDDE9 Set Permissions\u003C/h3\u003E\n\u003Cp\u003Eawait UAuthClient.Authorization.SetRolePermissionsAsync(new SetRolePermissionsRequest\n{\nRoleId = roleId,\nPermissions = new[]\n{\nPermission.From(\u0026quot;users.read\u0026quot;),\nPermission.From(\u0026quot;users.update\u0026quot;)\n}\n});\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Permissions support:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EExact match \u2192 users.create\u003C/li\u003E\n\u003Cli\u003EPrefix \u2192 users.*\u003C/li\u003E\n\u003Cli\u003EWildcard \u2192 *\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022delete-role\u0022\u003E\u274C Delete Role\u003C/h3\u003E\n\u003Cp\u003Eawait UAuthClient.Authorization.DeleteRoleAsync(new DeleteRoleRequest\n);\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Automatically removes role assignments from users\u003C/p\u003E\n\u003Ch3 id=\u0022assign-role-to-user\u0022\u003E\uD83D\uDC64 Assign Role to User\u003C/h3\u003E\n\u003Cp\u003Eawait UAuthClient.Authorization.AssignRoleToUserAsync(new AssignRoleRequest\n);\u003C/p\u003E\n\u003Ch3 id=\u0022remove-role\u0022\u003E\u2796 Remove Role\u003C/h3\u003E\n\u003Cp\u003Eawait UAuthClient.Authorization.RemoveRoleFromUserAsync(new RemoveRoleRequest\n);\u003C/p\u003E\n\u003Ch3 id=\u0022get-user-roles\u0022\u003E\uD83D\uDCCB Get User Roles\u003C/h3\u003E\n\u003Cp\u003Evar roles = await UAuthClient.Authorization.GetUserRolesAsync(userKey);\n\uD83D\uDD0D Check Authorization\nvar result = await UAuthClient.Authorization.CheckAsync(new AuthorizationCheckRequest\n);\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Returns:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAllow / Deny\u003C/li\u003E\n\u003Cli\u003EReason (if denied)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022permission-model\u0022\u003E\uD83E\uDDE0 Permission Model\u003C/h2\u003E\n\u003Cp\u003EPermissions are normalized and optimized:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EFull access \u2192 *\u003C/li\u003E\n\u003Cli\u003EGroup access \u2192 users.*\u003C/li\u003E\n\u003Cli\u003ESpecific \u2192 users.create\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Internally compiled for fast evaluation\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022security-notes\u0022\u003E\uD83D\uDD10 Security Notes\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EAuthorization is enforced server-side\u003C/li\u003E\n\u003Cli\u003EClient only requests actions\u003C/li\u003E\n\u003Cli\u003EPolicies may override permissions\u003C/li\u003E\n\u003Cli\u003ECross-tenant access is denied by default\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022summary\u0022\u003E\uD83C\uDFAF Summary\u003C/h2\u003E\n\u003Cp\u003EAuthorization in UltimateAuth:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Ecombines roles \u002B permissions \u002B policies\u003C/li\u003E\n\u003Cli\u003Eis evaluated through a decision pipeline\u003C/li\u003E\n\u003Cli\u003Esupports fine-grained and scalable access control\u003C/li\u003E\n\u003C/ul\u003E\n" +} \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/credentials.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/credentials.json new file mode 100644 index 00000000..631ec06d --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/credentials.json @@ -0,0 +1,5 @@ +{ + "Slug": "client/credentials", + "Title": "Credentials", + "Html": "\n\u003Cp\u003EThis section explains how to manage user credentials (such as passwords) using the UltimateAuth client.\u003C/p\u003E\n\u003Ch2 id=\u0022overview\u0022\u003E\uD83E\uDDE0 Overview\u003C/h2\u003E\n\u003Cp\u003ECredential operations are handled via:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003EUAuthClient.Credentials...\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThis includes:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Epassword management\u003C/li\u003E\n\u003Cli\u003Ecredential reset flows\u003C/li\u003E\n\u003Cli\u003Eadmin credential operations\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022change-password-self\u0022\u003E\uD83D\uDD10 Change Password (Self)\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Credentials.ChangeMyAsync(new ChangeCredentialRequest\n{\n CurrentSecret = \u0026quot;old-password\u0026quot;,\n NewSecret = \u0026quot;new-password\u0026quot;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Requires current password\u003Cbr /\u003E\n\uD83D\uDC49 Triggers \u003Ccode\u003ECredentialsChangedSelf\u003C/code\u003E event\u003C/p\u003E\n\u003Ch2 id=\u0022reset-password-self\u0022\u003E\uD83D\uDD01 Reset Password (Self)\u003C/h2\u003E\n\u003Ch3 id=\u0022begin-reset\u0022\u003EBegin Reset\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Evar begin = await UAuthClient.Credentials.BeginResetMyAsync(\n new BeginResetCredentialRequest\n {\n Identifier = \u0026quot;user@mail.com\u0026quot;\n });\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022complete-reset\u0022\u003EComplete Reset\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Credentials.CompleteResetMyAsync(\n new CompleteResetCredentialRequest\n {\n Token = \u0026quot;reset-token\u0026quot;,\n NewSecret = \u0026quot;new-password\u0026quot;\n });\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Typically used in:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eforgot password flows\u003C/li\u003E\n\u003Cli\u003Eemail-based reset flows\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022add-credential-self\u0022\u003E\u2795 Add Credential (Self)\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Credentials.AddMyAsync(new AddCredentialRequest\n{\n Secret = \u0026quot;password\u0026quot;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022revoke-credential-self\u0022\u003E\u274C Revoke Credential (Self)\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Credentials.RevokeMyAsync(new RevokeCredentialRequest\n{\n CredentialId = credentialId\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Useful for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eremoving login methods\u003C/li\u003E\n\u003Cli\u003Einvalidating compromised credentials\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022admin-change-user-credential\u0022\u003E\uD83D\uDC51 Admin: Change User Credential\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Credentials.ChangeUserAsync(userKey, new ChangeCredentialRequest\n{\n NewSecret = \u0026quot;new-password\u0026quot;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Does NOT require current password\u003C/p\u003E\n\u003Ch2 id=\u0022admin-add-credential\u0022\u003E\uD83D\uDC51 Admin: Add Credential\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Credentials.AddUserAsync(userKey, new AddCredentialRequest\n{\n Secret = \u0026quot;password\u0026quot;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022admin-revoke-credential\u0022\u003E\uD83D\uDC51 Admin: Revoke Credential\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Credentials.RevokeUserAsync(userKey, new RevokeCredentialRequest\n{\n CredentialId = credentialId\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022admin-reset-credential\u0022\u003E\uD83D\uDC51 Admin: Reset Credential\u003C/h2\u003E\n\u003Ch3 id=\u0022begin\u0022\u003EBegin\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Credentials.BeginResetUserAsync(userKey, request);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022complete\u0022\u003EComplete\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Credentials.CompleteResetUserAsync(userKey, request);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022delete-credential-admin\u0022\u003E\u274C Delete Credential (Admin)\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Credentials.DeleteUserAsync(userKey, new DeleteCredentialRequest\n{\n CredentialId = credentialId\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022credential-model\u0022\u003E\uD83E\uDDE0 Credential Model\u003C/h2\u003E\n\u003Cp\u003ECredentials are:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Euser-bound\u003C/li\u003E\n\u003Cli\u003Esecurity-version aware\u003C/li\u003E\n\u003Cli\u003Elifecycle-managed\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Changing credentials may:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Einvalidate sessions\u003C/li\u003E\n\u003Cli\u003Etrigger security updates\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022security-notes\u0022\u003E\uD83D\uDD10 Security Notes\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EPasswords are never sent back to client\u003C/li\u003E\n\u003Cli\u003EAll secrets are hashed server-side\u003C/li\u003E\n\u003Cli\u003EReset flows should be protected (email, OTP, etc.)\u003C/li\u003E\n\u003Cli\u003EAdmin operations are policy-protected\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022summary\u0022\u003E\uD83C\uDFAF Summary\u003C/h2\u003E\n\u003Cp\u003ECredential management in UltimateAuth:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Esupports self-service and admin flows\u003C/li\u003E\n\u003Cli\u003Eintegrates with security lifecycle\u003C/li\u003E\n\u003Cli\u003Eenables safe credential rotation\u003C/li\u003E\n\u003C/ul\u003E\n" +} \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/identifiers.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/identifiers.json new file mode 100644 index 00000000..d413e344 --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/identifiers.json @@ -0,0 +1,5 @@ +{ + "Slug": "client/identifiers", + "Title": "Identifiers", + "Html": "\n\u003Cp\u003EThis section explains how UltimateAuth manages user identifiers such as email, username, and phone.\u003C/p\u003E\n\u003Ch2 id=\u0022overview\u0022\u003E\uD83E\uDDE0 Overview\u003C/h2\u003E\n\u003Cp\u003EIn UltimateAuth, identifiers are \u003Cstrong\u003Efirst-class entities\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EThey are NOT just fields on the user.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 On the client, you interact with:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003EUAuthClient.Identifiers...\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022what-is-an-identifier\u0022\u003E\uD83D\uDD11 What is an Identifier?\u003C/h2\u003E\n\u003Cp\u003EAn identifier represents a way to identify a user:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEmail\u003C/li\u003E\n\u003Cli\u003EUsername\u003C/li\u003E\n\u003Cli\u003EPhone number\u003C/li\u003E\n\u003Cli\u003ECustom identifiers\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Each identifier has:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EValue (e.g. user@ultimateauth.com)\u003C/li\u003E\n\u003Cli\u003EType (email, username, etc.)\u003C/li\u003E\n\u003Cli\u003EVerification state\u003C/li\u003E\n\u003Cli\u003EPrimary flag\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022primary-identifier\u0022\u003E\u2B50 Primary Identifier\u003C/h2\u003E\n\u003Cp\u003EA user can have multiple identifiers, but only one can be \u003Cstrong\u003Eprimary\u003C/strong\u003E. Setting an identifier to primary automatically unset the current primary identifier if exists.\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Identifiers.SetMyPrimaryAsync(new SetPrimaryUserIdentifierRequest\n{\n Id = identifierId\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Primary identifier is typically:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUsed for display\u003C/li\u003E\n\u003Cli\u003EPreferred for communication\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022login-identifiers\u0022\u003E\uD83D\uDD10 Login Identifiers\u003C/h2\u003E\n\u003Cp\u003ENot all identifiers are used for login.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Cstrong\u003ELogin identifiers are a subset of identifiers\u003C/strong\u003E\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 This is configurable:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEnable/disable per type\u003C/li\u003E\n\u003Cli\u003ECustom logic can be applied\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022get-my-identifiers\u0022\u003E\uD83D\uDCCB Get My Identifiers\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Evar result = await UAuthClient.Identifiers.GetMyAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022add-identifier\u0022\u003E\u2795 Add Identifier\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Identifiers.AddMyAsync(new AddUserIdentifierRequest\n{\n Identifier = \u0026quot;new@ultimateauth.com\u0026quot;,\n Type = UserIdentifierType.Email,\n IsPrimary = true\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022update-identifier\u0022\u003E\u270F\uFE0F Update Identifier\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003EUpdateUserIdentifierRequest updateRequest = new()\n{\n Id = item.Id,\n NewValue = item.Value\n};\n\nawait UAuthClient.Identifiers.UpdateMyAsync(updateRequest);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022verify-identifier\u0022\u003E\u2705 Verify Identifier\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Identifiers.VerifyMyAsync(new VerifyUserIdentifierRequest\n{\n Id = identifierId\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022delete-identifier\u0022\u003E\u274C Delete Identifier\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Identifiers.DeleteMyAsync(new DeleteUserIdentifierRequest\n{\n Id = identifierId\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022admin-operations\u0022\u003E\uD83D\uDC64 Admin Operations\u003C/h2\u003E\n\u003Ch3 id=\u0022get-user-identifiers\u0022\u003EGet User Identifiers\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Identifiers.GetUserAsync(userKey);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022add-identifier-to-user\u0022\u003EAdd Identifier to User\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Identifiers.AddUserAsync(userKey, request);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022update-identifier-1\u0022\u003EUpdate Identifier\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Identifiers.UpdateUserAsync(userKey, request);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022delete-identifier-1\u0022\u003EDelete Identifier\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Identifiers.DeleteUserAsync(userKey, request);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022state-events\u0022\u003E\uD83D\uDD04 State Events\u003C/h2\u003E\n\u003Cp\u003EIdentifier changes trigger events:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EIdentifiersChanged\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Useful for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUI updates\u003C/li\u003E\n\u003Cli\u003ECache invalidation\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022security-considerations\u0022\u003E\uD83D\uDD10 Security Considerations\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EIdentifiers may require verification\u003C/li\u003E\n\u003Cli\u003ELogin identifiers can be restricted\u003C/li\u003E\n\u003Cli\u003EPrimary identifier can be controlled\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022summary\u0022\u003E\uD83C\uDFAF Summary\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth identifiers:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eare independent entities\u003C/li\u003E\n\u003Cli\u003Esupport multiple types\u003C/li\u003E\n\u003Cli\u003Eseparate login vs non-login identifiers\u003C/li\u003E\n\u003Cli\u003Eare fully manageable via client\u003C/li\u003E\n\u003C/ul\u003E\n" +} \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/index.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/index.json new file mode 100644 index 00000000..6d4ae5d6 --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/index.json @@ -0,0 +1,5 @@ +{ + "Slug": "client/index", + "Title": "Client \u0026 API", + "Html": "\n\u003Cp\u003EUltimateAuth Client is a \u003Cstrong\u003Ehigh-level SDK\u003C/strong\u003E designed to simplify authentication flows.\u003C/p\u003E\n\u003Cp\u003EIt is NOT just an HTTP wrapper.\u003C/p\u003E\n\u003Ch2 id=\u0022what-makes-the-client-different\u0022\u003E\uD83E\uDDE0 What Makes the Client Different?\u003C/h2\u003E\n\u003Cp\u003EThe client:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EHandles full authentication flows (login, PKCE, refresh)\u003C/li\u003E\n\u003Cli\u003EManages redirects automatically\u003C/li\u003E\n\u003Cli\u003EPublishes state events\u003C/li\u003E\n\u003Cli\u003EProvides structured results\u003C/li\u003E\n\u003Cli\u003EWorks across multiple client types (SPA, server, hybrid)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You SHOULD use the client instead of calling endpoints manually.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022client-architecture\u0022\u003E\uD83E\uDDF1 Client Architecture\u003C/h2\u003E\n\u003Cp\u003EThe client is split into multiple specialized services:\u003C/p\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EService\u003C/th\u003E\n\u003Cth\u003EResponsibility\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EFlowClient\u003C/td\u003E\n\u003Ctd\u003ELogin, logout, refresh, PKCE\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ESessionClient\u003C/td\u003E\n\u003Ctd\u003ESession \u0026amp; device management\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EUserClient\u003C/td\u003E\n\u003Ctd\u003EUser profile \u0026amp; lifecycle\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EIdentifierClient\u003C/td\u003E\n\u003Ctd\u003EEmail / username / phone management\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ECredentialClient\u003C/td\u003E\n\u003Ctd\u003EPassword management\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EAuthorizationClient\u003C/td\u003E\n\u003Ctd\u003ERoles \u0026amp; permissions\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022client-entry-point\u0022\u003E\uD83E\uDDE9 Client Entry Point\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth exposes a single entry point:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ccode\u003EUAuthClient\u003C/code\u003E\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003E[Inject] IUAuthClient UAuthClient { get; set; } = null!;\n\nawait UAuthClient.Flows.LoginAsync(...);\nawait UAuthClient.Users.GetMeAsync();\nawait UAuthClient.Sessions.GetMyChainsAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022core-concept-flow-based-design\u0022\u003E\uD83D\uDD11 Core Concept: Flow-Based Design\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth is \u003Cstrong\u003Eflow-oriented\u003C/strong\u003E, not endpoint-oriented.\u003C/p\u003E\n\u003Cp\u003EInstead of calling endpoints:\u003C/p\u003E\n\u003Cp\u003E\u274C POST /auth/login\u003Cbr /\u003E\n\u2714 flowClient.LoginAsync()\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022example\u0022\u003E\u26A1 Example\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Flows.LoginAsync(new LoginRequest\n{\n Identifier = \u0026quot;user@ultimateauth.com\u0026quot;,\n Secret = \u0026quot;password\u0026quot;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This automatically:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EBuilds request payload\u003C/li\u003E\n\u003Cli\u003EHandles redirect\u003C/li\u003E\n\u003Cli\u003EIntegrates with configured endpoints\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022state-events\u0022\u003E\uD83D\uDD04 State Events\u003C/h2\u003E\n\u003Cp\u003EClient automatically publishes events:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESessionRevoked\u003C/li\u003E\n\u003Cli\u003EProfileChanged\u003C/li\u003E\n\u003Cli\u003EAuthorizationChanged\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022how-to-use-this-section\u0022\u003E\uD83E\uDDED How to Use This Section\u003C/h2\u003E\n\u003Cp\u003EFollow these guides:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eauthentication.md \u2192 login, refresh, logout\u003C/li\u003E\n\u003Cli\u003Esession-management.md \u2192 sessions \u0026amp; devices\u003C/li\u003E\n\u003Cli\u003Euser-management.md \u2192 user operations\u003C/li\u003E\n\u003Cli\u003Eidentifiers.md \u2192 login identifiers\u003C/li\u003E\n\u003Cli\u003Eauthorization.md \u2192 roles \u0026amp; permissions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022summary\u0022\u003E\uD83C\uDFAF Summary\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth Client:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eabstracts complexity\u003C/li\u003E\n\u003Cli\u003Eenforces correct flows\u003C/li\u003E\n\u003Cli\u003Ereduces security mistakes\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Think of it as:\u003C/p\u003E\n\u003Cp\u003E\u003Cstrong\u003E\u201CAuthentication runtime for your frontend / app\u201D\u003C/strong\u003E\u003C/p\u003E\n" +} \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/session-management.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/session-management.json new file mode 100644 index 00000000..2cf70109 --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/session-management.json @@ -0,0 +1,5 @@ +{ + "Slug": "client/session-management", + "Title": "Session Management", + "Html": "\n\u003Cp\u003EThis section explains how to manage sessions and devices using the UltimateAuth client.\u003C/p\u003E\n\u003Ch2 id=\u0022overview\u0022\u003E\uD83E\uDDE0 Overview\u003C/h2\u003E\n\u003Cp\u003EIn UltimateAuth, sessions are \u003Cstrong\u003Enot just tokens\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EThey are structured as:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERoot \u2192 user-level security\u003C/li\u003E\n\u003Cli\u003EChain \u2192 device (browser / mobile / app)\u003C/li\u003E\n\u003Cli\u003ESession \u2192 individual authentication instance\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 On the client, you interact with:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003E[Inject] IUAuthClient UAuthClient { get; set; } = null!;\n\nawait UAuthClient.Sessions...\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022get-active-sessions-devices\u0022\u003E\uD83D\uDCCB Get Active Sessions (Devices)\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Evar result = await UAuthClient.Sessions.GetMyChainsAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Returns:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EActive devices\u003C/li\u003E\n\u003Cli\u003ESession chains\u003C/li\u003E\n\u003Cli\u003EMetadata (device, timestamps)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022get-session-detail\u0022\u003E\uD83D\uDD0D Get Session Detail\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Evar detail = await UAuthClient.Sessions.GetMyChainDetailAsync(chainId);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Use this to:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EInspect a specific device\u003C/li\u003E\n\u003Cli\u003EView session history\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022logout-vs-revoke-important\u0022\u003E\uD83D\uDEAA Logout vs Revoke (Important)\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth distinguishes between:\u003C/p\u003E\n\u003Ch3 id=\u0022logout\u0022\u003ELogout\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Flows.LogoutAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cul\u003E\n\u003Cli\u003EEnds \u003Cstrong\u003Ecurrent session\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EUser can login again normally\u003C/li\u003E\n\u003Cli\u003EDoes NOT affect other devices\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022revoke-session-control\u0022\u003ERevoke (Session Control)\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Sessions.RevokeMyChainAsync(chainId);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cul\u003E\n\u003Cli\u003EInvalidates entire \u003Cstrong\u003Edevice chain\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EAll sessions under that device are revoked\u003C/li\u003E\n\u003Cli\u003ECannot be restored\u003C/li\u003E\n\u003Cli\u003ENew login creates a new chain\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Key difference:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogout = end current session\u003C/li\u003E\n\u003Cli\u003ERevoke = destroy device identity\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EFor standard auth process, use \u003Ccode\u003EUAuthClient.Flows.LogoutMyDeviceAsync(chainId)\u003C/code\u003E instead of \u003Ccode\u003EUAuthClient.Sessions.RevokeMyChainAsync(chainId)\u003C/code\u003E\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022revoke-other-devices\u0022\u003E\uD83D\uDCF1 Revoke Other Devices\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Sessions.RevokeMyOtherChainsAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EKeeps current device active\u003C/li\u003E\n\u003Cli\u003ELogs out all other devices\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022revoke-all-sessions\u0022\u003E\uD83D\uDCA5 Revoke All Sessions\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Sessions.RevokeAllMyChainsAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogs out ALL devices (including current)\u003C/li\u003E\n\u003Cli\u003EForces full reauthentication everywhere\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022admin-session-management\u0022\u003E\uD83D\uDC64 Admin Session Management\u003C/h2\u003E\n\u003Ch3 id=\u0022get-user-devices\u0022\u003EGet User Devices\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Sessions.GetUserChainsAsync(userKey);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022revoke-specific-session\u0022\u003ERevoke Specific Session\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Sessions.RevokeUserSessionAsync(userKey, sessionId);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022revoke-device-chain\u0022\u003ERevoke Device (Chain)\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Sessions.RevokeUserChainAsync(userKey, chainId);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022revoke-all-user-sessions\u0022\u003ERevoke All User Sessions\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Sessions.RevokeAllUserChainsAsync(userKey);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022device-model\u0022\u003E\uD83E\uDDE0 Device Model\u003C/h2\u003E\n\u003Cp\u003EEach chain represents a \u003Cstrong\u003Edevice identity\u003C/strong\u003E:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EBrowser instance\u003C/li\u003E\n\u003Cli\u003EMobile device\u003C/li\u003E\n\u003Cli\u003EApplication instance\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Sessions are grouped under chains.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022security-implications\u0022\u003E\uD83D\uDD10 Security Implications\u003C/h2\u003E\n\u003Cp\u003ESession operations are security-critical:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERevoke is irreversible\u003C/li\u003E\n\u003Cli\u003EDevice isolation is enforced\u003C/li\u003E\n\u003Cli\u003ECross-device attacks are contained\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022summary\u0022\u003E\uD83C\uDFAF Summary\u003C/h2\u003E\n\u003Cp\u003ESession management in UltimateAuth:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eis device-aware\u003C/li\u003E\n\u003Cli\u003Eseparates logout vs revoke\u003C/li\u003E\n\u003Cli\u003Egives full control over user sessions\u003C/li\u003E\n\u003C/ul\u003E\n" +} \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/user-management.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/user-management.json new file mode 100644 index 00000000..cc177173 --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/user-management.json @@ -0,0 +1,5 @@ +{ + "Slug": "client/user-management", + "Title": "User Management", + "Html": "\n\u003Cp\u003EThis section explains how to manage users using the UltimateAuth client.\u003C/p\u003E\n\u003Ch2 id=\u0022overview\u0022\u003E\uD83E\uDDE0 Overview\u003C/h2\u003E\n\u003Cp\u003EUser operations are handled via:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003EUAuthClient.Users...\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThis includes:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EProfile management\u003C/li\u003E\n\u003Cli\u003EUser lifecycle\u003C/li\u003E\n\u003Cli\u003EAdmin operations\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022get-current-user\u0022\u003E\uD83D\uDE4B Get Current User\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Evar me = await UAuthClient.Users.GetMeAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Returns:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUser profile\u003C/li\u003E\n\u003Cli\u003EStatus\u003C/li\u003E\n\u003Cli\u003EBasic identity data\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022update-profile\u0022\u003E\u270F\uFE0F Update Profile\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Users.UpdateMeAsync(new UpdateProfileRequest\n{\n DisplayName = \u0026quot;John Doe\u0026quot;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Triggers:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EProfile update\u003C/li\u003E\n\u003Cli\u003EState event (ProfileChanged)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022delete-current-user\u0022\u003E\u274C Delete Current User\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Users.DeleteMeAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EDeletes user (based on configured mode)\u003C/li\u003E\n\u003Cli\u003EEnds session\u003C/li\u003E\n\u003Cli\u003ETriggers state update\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022admin-query-users\u0022\u003E\uD83D\uDC51 Admin: Query Users\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Evar result = await UAuthClient.Users.QueryAsync(new UserQuery\n{\n Search = \u0026quot;john\u0026quot;,\n PageNumber = 1,\n PageSize = 10\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Supports:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Esearch\u003C/li\u003E\n\u003Cli\u003Epagination\u003C/li\u003E\n\u003Cli\u003Efiltering (status, etc.)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022create-user\u0022\u003E\u2795 Create User\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Users.CreateAsync(new CreateUserRequest\n{\n UserName = \u0026quot;john\u0026quot;,\n Email = \u0026quot;john@mail.com\u0026quot;,\n Password = \u0026quot;123456\u0026quot;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022admin-create\u0022\u003E\uD83D\uDEE0 Admin Create\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Users.CreateAsAdminAsync(request);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022change-status\u0022\u003E\uD83D\uDD04 Change Status\u003C/h2\u003E\n\u003Ch3 id=\u0022self\u0022\u003ESelf\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Users.ChangeMyStatusAsync(new ChangeUserStatusSelfRequest\n{\n Status = UserStatus.SelfSuspended\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022admin\u0022\u003EAdmin\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Users.ChangeUserStatusAsync(userKey, new ChangeUserStatusAdminRequest\n{\n Status = UserStatus.Suspended\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022delete-user-admin\u0022\u003E\u274C Delete User (Admin)\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Users.DeleteUserAsync(userKey, new DeleteUserRequest\n{\n Mode = DeleteMode.Soft\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022get-user\u0022\u003E\uD83D\uDD0D Get User\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Evar user = await UAuthClient.Users.GetUserAsync(userKey);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022update-user-admin\u0022\u003E\u270F\uFE0F Update User (Admin)\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Users.UpdateUserAsync(userKey, request);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022lifecycle-model\u0022\u003E\uD83E\uDDE0 Lifecycle Model\u003C/h2\u003E\n\u003Cp\u003EUsers have a lifecycle:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EActive\u003C/li\u003E\n\u003Cli\u003ESuspended\u003C/li\u003E\n\u003Cli\u003EDisabled\u003C/li\u003E\n\u003Cli\u003EDeleted (soft/hard)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Status impacts:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Elogin ability\u003C/li\u003E\n\u003Cli\u003Esession validity\u003C/li\u003E\n\u003Cli\u003Eauthorization\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022security-notes\u0022\u003E\uD83D\uDD10 Security Notes\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EStatus changes may invalidate sessions\u003C/li\u003E\n\u003Cli\u003EDelete may trigger cleanup across domains\u003C/li\u003E\n\u003Cli\u003EAdmin actions are policy-protected\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022summary\u0022\u003E\uD83C\uDFAF Summary\u003C/h2\u003E\n\u003Cp\u003EUser management in UltimateAuth:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eis lifecycle-aware\u003C/li\u003E\n\u003Cli\u003Esupports self \u002B admin flows\u003C/li\u003E\n\u003Cli\u003Eintegrates with session \u0026amp; security model\u003C/li\u003E\n\u003C/ul\u003E\n" +} \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/advanced-configuration.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/advanced-configuration.json new file mode 100644 index 00000000..289ac798 --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/advanced-configuration.json @@ -0,0 +1,5 @@ +{ + "Slug": "configuration/advanced-configuration", + "Title": "Advanced Configuration", + "Html": "\n\u003Cp\u003EUltimateAuth is designed to be flexible \u2014 but not fragile.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 You can customize almost every part of the system\u003Cbr /\u003E\n\uD83D\uDC49 Without breaking its security guarantees\u003C/p\u003E\n\u003Ch2 id=\u0022philosophy\u0022\u003E\u26A0\uFE0F Philosophy\u003C/h2\u003E\n\u003Cp\u003ECustomization in UltimateAuth follows one rule:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 You can extend behavior\u003Cbr /\u003E\n\uD83D\uDC49 You should not bypass security\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022extension-points\u0022\u003E\uD83E\uDDE9 Extension Points\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth exposes multiple extension points:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EResolvers\u003C/li\u003E\n\u003Cli\u003EValidators\u003C/li\u003E\n\u003Cli\u003EAuthorities\u003C/li\u003E\n\u003Cli\u003EOrchestrators\u003C/li\u003E\n\u003Cli\u003EStores\u003C/li\u003E\n\u003Cli\u003EEvents\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You don\u2019t replace the system\u003Cbr /\u003E\n\uD83D\uDC49 You plug into it\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022replacing-services\u0022\u003E\uD83D\uDD0C Replacing Services\u003C/h2\u003E\n\u003Cp\u003EAll core services can be overridden using DI:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eservices.AddScoped\u0026lt;ISessionValidator, CustomSessionValidator\u0026gt;();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This allows deep customization\u003Cbr /\u003E\n\uD83D\uDC49 While preserving the pipeline\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022authorities-decisions\u0022\u003E\uD83E\uDDE0 Authorities \u0026amp; Decisions\u003C/h2\u003E\n\u003Cp\u003EAuthorities are responsible for decisions:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELoginAuthority\u003C/li\u003E\n\u003Cli\u003EAccessAuthority\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Cp\u003EYou can override them:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eservices.AddScoped\u0026lt;ILoginAuthority, CustomLoginAuthority\u0026gt;();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This changes decision logic\u003Cbr /\u003E\n\uD83D\uDC49 Without touching flows\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022orchestrators\u0022\u003E\uD83D\uDD04 Orchestrators\u003C/h2\u003E\n\u003Cp\u003EOrchestrators coordinate execution:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EValidate\u003C/li\u003E\n\u003Cli\u003EAuthorize\u003C/li\u003E\n\u003Cli\u003EExecute command\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 They enforce invariants\u003Cbr /\u003E\n\uD83D\uDC49 Do not bypass them, unless you exact know what you are doing\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022store-customization\u0022\u003E\uD83D\uDDC4 Store Customization\u003C/h2\u003E\n\u003Cp\u003EYou can provide custom stores:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession store\u003C/li\u003E\n\u003Cli\u003ERefresh token store\u003C/li\u003E\n\u003Cli\u003EUser store\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Supports EF Core, in-memory, or custom implementations\u003C/p\u003E\n\u003Ch2 id=\u0022events\u0022\u003E\uD83D\uDCE1 Events\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth provides event hooks:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogin\u003C/li\u003E\n\u003Cli\u003ELogout\u003C/li\u003E\n\u003Cli\u003ERefresh\u003C/li\u003E\n\u003Cli\u003ERevoke\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.Events.OnUserLoggedIn = ctx =\u0026gt;\n{\n // custom logic\n return Task.CompletedTask;\n};\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Use events for side-effects\u003Cbr /\u003E\n\uD83D\uDC49 Not for core logic\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mode-configuration-overrides\u0022\u003E\u2699\uFE0F Mode Configuration Overrides\u003C/h2\u003E\n\u003Cp\u003EYou can customize behavior per mode:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.ModeConfigurations[UAuthMode.Hybrid] = options =\u0026gt;\n{\n options.Token.AccessTokenLifetime = TimeSpan.FromMinutes(5);\n};\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Runs after defaults\u003Cbr /\u003E\n\uD83D\uDC49 Allows fine-grained control\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022custom-resolvers\u0022\u003E\uD83D\uDD10 Custom Resolvers\u003C/h2\u003E\n\u003Cp\u003EYou can override how data is resolved:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ETenant resolver\u003C/li\u003E\n\u003Cli\u003ESession resolver\u003C/li\u003E\n\u003Cli\u003EDevice resolver\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Enables full control over request interpretation\u003C/p\u003E\n\u003Ch2 id=\u0022safety-boundaries\u0022\u003E\uD83D\uDEE1 Safety Boundaries\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth enforces:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EInvariants\u003C/li\u003E\n\u003Cli\u003EValidation\u003C/li\u003E\n\u003Cli\u003EFail-fast behavior\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Unsafe overrides will fail early\u003C/p\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Extend the system\u003Cbr /\u003E\n\uD83D\uDC49 Don\u2019t fight the system\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch2 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EEverything is replaceable via DI\u003C/li\u003E\n\u003Cli\u003EAuthorities control decisions\u003C/li\u003E\n\u003Cli\u003EOrchestrators enforce flow\u003C/li\u003E\n\u003Cli\u003EEvents are for side-effects\u003C/li\u003E\n\u003Cli\u003ESecurity boundaries are protected\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Ch2 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EReturn to \u003Cstrong\u003EAuth Flows\u003C/strong\u003E or explore \u003Cstrong\u003EPlugin Domains\u003C/strong\u003E\u003C/p\u003E\n" +} \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/client-options.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/client-options.json new file mode 100644 index 00000000..ebfda14d --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/client-options.json @@ -0,0 +1,5 @@ +{ + "Slug": "configuration/client-options", + "Title": "Client Options", + "Html": "\n\u003Cp\u003EClient Options define how UltimateAuth behaves on the \u003Cstrong\u003Eclient side\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 While Server controls the system,\u003Cbr /\u003E\n\uD83D\uDC49 Client controls how it is \u003Cstrong\u003Eused\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022what-are-client-options\u0022\u003E\uD83E\uDDE0 What Are Client Options?\u003C/h2\u003E\n\u003Cp\u003EClient options configure:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EClient profile (WASM, Server, MAUI, API)\u003C/li\u003E\n\u003Cli\u003EEndpoint communication\u003C/li\u003E\n\u003Cli\u003EPKCE behavior\u003C/li\u003E\n\u003Cli\u003EToken refresh\u003C/li\u003E\n\u003Cli\u003ERe-authentication behavior\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022basic-usage\u0022\u003E\u2699\uFE0F Basic Usage\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthClientBlazor(o =\u0026gt;\n{\n o.AutoDetectClientProfile = false;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022client-profile\u0022\u003E\uD83E\uDDE9 Client Profile\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth automatically detects client type by default:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EBlazor WASM\u003C/li\u003E\n\u003Cli\u003EBlazor Server\u003C/li\u003E\n\u003Cli\u003EMAUI\u003C/li\u003E\n\u003Cli\u003EWebServer\u003C/li\u003E\n\u003Cli\u003EAPI\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EYou can override manually:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.ClientProfile = UAuthClientProfile.BlazorWasm;\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Manual override is useful for testing or special scenarios\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022endpoints\u0022\u003E\uD83C\uDF10 Endpoints\u003C/h2\u003E\n\u003Cp\u003EDefines where requests are sent:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.Endpoints.BasePath = \u0026quot;https://localhost:5001/auth\u0026quot;;\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Required for WASM / remote clients\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022pkce-configuration\u0022\u003E\uD83D\uDD10 PKCE Configuration\u003C/h2\u003E\n\u003Cp\u003EUsed for browser-based login flows:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.Pkce.ReturnUrl = \u0026quot;https://localhost:5002/home\u0026quot;;\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Required for WASM scenarios\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022auto-refresh\u0022\u003E\uD83D\uDD01 Auto Refresh\u003C/h2\u003E\n\u003Cp\u003EControls token/session refresh behavior:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.AutoRefresh.Interval = TimeSpan.FromMinutes(1);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Keeps authentication alive automatically\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022re-authentication\u0022\u003E\uD83D\uDD04 Re-authentication\u003C/h2\u003E\n\u003Cp\u003EControls behavior when session expires:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.Reauth.Behavior = ReauthBehavior.RaiseEvent;\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Allows silent or interactive re-login\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Server decides\u003Cbr /\u003E\n\uD83D\uDC49 Client adapts\u003C/p\u003E\n\u003Ch2 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EClient options control runtime behavior\u003C/li\u003E\n\u003Cli\u003EProfile detection is automatic\u003C/li\u003E\n\u003Cli\u003EPKCE is required for public clients\u003C/li\u003E\n\u003Cli\u003ERefresh and re-auth are configurable\u003C/li\u003E\n\u003Cli\u003EWorks together with Server options\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Ch2 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003EConfiguration Sources\u003C/strong\u003E\u003C/p\u003E\n" +} \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/configuration-overview.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/configuration-overview.json new file mode 100644 index 00000000..e622b782 --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/configuration-overview.json @@ -0,0 +1,5 @@ +{ + "Slug": "configuration/configuration-overview", + "Title": "Configuration Overview", + "Html": "\n\u003Cp\u003EUltimateAuth is not configured as a static system.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 It is configured as a \u003Cstrong\u003Eruntime-adaptive system\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022a-common-misunderstanding\u0022\u003E\u26A0\uFE0F A Common Misunderstanding\u003C/h2\u003E\n\u003Cp\u003EMany frameworks expect you to configure authentication once:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EChoose JWT or cookies\u003C/li\u003E\n\u003Cli\u003ESet token lifetimes\u003C/li\u003E\n\u003Cli\u003EConfigure behavior globally\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 And that configuration applies everywhere\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 UltimateAuth does NOT work like this\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022layered-configuration-model\u0022\u003E\uD83E\uDDE9 Layered Configuration Model\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth separates configuration into distinct layers:\u003C/p\u003E\n\u003Ch3 id=\u0022core-behavior-definition\u0022\u003E\uD83D\uDD39 Core (Behavior Definition)\u003C/h3\u003E\n\u003Cp\u003ECore defines \u003Cstrong\u003Ewhat authentication means\u003C/strong\u003E:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession lifecycle\u003C/li\u003E\n\u003Cli\u003EToken behavior\u003C/li\u003E\n\u003Cli\u003EPKCE rules\u003C/li\u003E\n\u003Cli\u003EMulti-tenant handling\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Core is the foundation\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 But you typically do NOT configure it directly\u003C/p\u003E\n\u003Ch3 id=\u0022server-application-configuration\u0022\u003E\uD83D\uDD39 Server (Application Configuration)\u003C/h3\u003E\n\u003Cp\u003EServer is where you configure the system:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthServer(o =\u0026gt;\n{\n o.Login.MaxFailedAttempts = 5;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThis layer controls:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAllowed authentication modes\u003C/li\u003E\n\u003Cli\u003EEndpoint exposure\u003C/li\u003E\n\u003Cli\u003ECookie behavior\u003C/li\u003E\n\u003Cli\u003ESecurity policies\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This is your main configuration surface\u003C/p\u003E\n\u003Ch3 id=\u0022client-runtime-behavior\u0022\u003E\uD83D\uDD39 Client (Runtime Behavior)\u003C/h3\u003E\n\u003Cp\u003EClient configuration controls:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EClient profile (WASM, Server, MAUI, API)\u003C/li\u003E\n\u003Cli\u003EPKCE behavior\u003C/li\u003E\n\u003Cli\u003EAuto-refresh\u003C/li\u003E\n\u003Cli\u003ERe-authentication\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Client influences how flows are executed\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022runtime-configuration-the-key-idea\u0022\u003E\u26A1 Runtime Configuration (The Key Idea)\u003C/h2\u003E\n\u003Cp\u003EHere is the most important concept:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 UltimateAuth does NOT use configuration as-is\u003C/p\u003E\n\u003Cp\u003EInstead, it computes \u003Cstrong\u003EEffective Configuration per request\u003C/strong\u003E\u003C/p\u003E\n\u003Ch3 id=\u0022how-it-works\u0022\u003E\uD83E\uDDE0 How It Works\u003C/h3\u003E\n\u003Cp\u003EAt runtime:\u003C/p\u003E\n\u003Col\u003E\n\u003Cli\u003EClient profile is detected\u003C/li\u003E\n\u003Cli\u003EFlow type is determined (Login, Refresh, etc.)\u003C/li\u003E\n\u003Cli\u003EAuth mode is resolved\u003C/li\u003E\n\u003Cli\u003EDefaults are applied\u003C/li\u003E\n\u003Cli\u003EMode-specific overrides are applied\u003C/li\u003E\n\u003C/ol\u003E\n\u003Cp\u003E\uD83D\uDC49 This produces:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003EEffectiveOptions\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022from-static-to-dynamic\u0022\u003E\uD83D\uDD04 From Static to Dynamic\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode\u003EUAuthServerOptions (startup)\n \u2193\nMode Resolver\n \u2193\nApply Defaults\n \u2193\nMode Overrides\n \u2193\nEffectiveUAuthServerOptions (runtime)\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Every request can have a different effective configuration\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022why-this-matters\u0022\u003E\uD83C\uDFAF Why This Matters\u003C/h2\u003E\n\u003Cp\u003EThis allows UltimateAuth to:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUse different auth modes per client\u003C/li\u003E\n\u003Cli\u003EAdapt behavior per flow\u003C/li\u003E\n\u003Cli\u003EEnforce security dynamically\u003C/li\u003E\n\u003Cli\u003EAvoid global misconfiguration\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You don\u2019t configure \u201Cone system\u201D\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 You configure a \u003Cstrong\u003Edecision engine\u003C/strong\u003E\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022safety-by-design\u0022\u003E\uD83D\uDEE1 Safety by Design\u003C/h2\u003E\n\u003Cp\u003EEven with dynamic behavior:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EInvalid combinations fail early\u003C/li\u003E\n\u003Cli\u003EDisallowed modes are rejected\u003C/li\u003E\n\u003Cli\u003ESecurity invariants are enforced\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Flexibility does not reduce safety\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022core-vs-effective\u0022\u003E\u2699\uFE0F Core vs Effective\u003C/h2\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EConcept\u003C/th\u003E\n\u003Cth\u003EMeaning\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ECore Options\u003C/td\u003E\n\u003Ctd\u003EBase behavior definitions\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EServer Options\u003C/td\u003E\n\u003Ctd\u003EApplication-level configuration\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EEffective Options\u003C/td\u003E\n\u003Ctd\u003ERuntime-resolved behavior\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Cp\u003E\uD83D\uDC49 Effective options are what actually run\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 You don\u2019t configure authentication\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 You configure how it is \u003Cstrong\u003Eresolved\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EControl runtime \u2192 Server Options\u003C/li\u003E\n\u003Cli\u003EConfigure clients \u2192 Client Options\u003C/li\u003E\n\u003C/ul\u003E\n" +} \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/configuration-sources.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/configuration-sources.json new file mode 100644 index 00000000..0382fd0d --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/configuration-sources.json @@ -0,0 +1,5 @@ +{ + "Slug": "configuration/configuration-sources", + "Title": "Configuration Sources", + "Html": "\n\u003Cp\u003EUltimateAuth supports multiple configuration sources.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 But more importantly, it defines a \u003Cstrong\u003Eclear and predictable precedence model\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022two-ways-to-configure\u0022\u003E\uD83E\uDDE0 Two Ways to Configure\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth can be configured using:\u003C/p\u003E\n\u003Ch3 id=\u0022code-program.cs\u0022\u003E\uD83D\uDD39 Code (Program.cs)\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthServer(o =\u0026gt;\n{\n o.Login.MaxFailedAttempts = 5;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022configuration-files-appsettings.json\u0022\u003E\uD83D\uDD39 Configuration Files (appsettings.json)\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-json\u0022\u003E{\n \u0026quot;UltimateAuth\u0026quot;: {\n \u0026quot;Server\u0026quot;: {\n \u0026quot;Login\u0026quot;: {\n \u0026quot;MaxFailedAttempts\u0026quot;: 5\n }\n }\n }\n}\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022precedence-rules\u0022\u003E\u2696\uFE0F Precedence Rules\u003C/h2\u003E\n\u003Cp\u003EThis is the most important rule:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Cstrong\u003EConfiguration files override code\u003C/strong\u003E\u003C/p\u003E\n\u003Cp\u003EExecution order:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003EProgram.cs configuration\n \u2193\nappsettings.json binding\n \u2193\nFinal options\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This means:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EDefaults can be defined in code\u003C/li\u003E\n\u003Cli\u003EEnvironments can override them safely\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022environment-based-configuration\u0022\u003E\uD83C\uDF0D Environment-Based Configuration\u003C/h2\u003E\n\u003Cp\u003EASP.NET Core supports environment-specific configuration:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eappsettings.Development.json\u003C/li\u003E\n\u003Cli\u003Eappsettings.Staging.json\u003C/li\u003E\n\u003Cli\u003Eappsettings.Production.json\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EExample:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-json\u0022\u003E{\n \u0026quot;UltimateAuth\u0026quot;: {\n \u0026quot;Server\u0026quot;: {\n \u0026quot;Session\u0026quot;: {\n \u0026quot;IdleTimeout\u0026quot;: \u0026quot;7.00:00:00\u0026quot;\n }\n }\n }\n}\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 You can use different values per environment without changing code\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022recommended-strategy\u0022\u003E\uD83E\uDDE9 Recommended Strategy\u003C/h2\u003E\n\u003Cp\u003EFor real-world applications:\u003C/p\u003E\n\u003Ch3 id=\u0022use-program.cs-for\u0022\u003E\u2714 Use Program.cs for:\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EDefaults\u003C/li\u003E\n\u003Cli\u003EDevelopment setup\u003C/li\u003E\n\u003Cli\u003ELocal testing\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022use-appsettings-for\u0022\u003E\u2714 Use appsettings for:\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EEnvironment-specific overrides\u003C/li\u003E\n\u003Cli\u003EProduction tuning\u003C/li\u003E\n\u003Cli\u003EDeployment configuration\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This keeps your system flexible and maintainable\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022safety-validation\u0022\u003E\uD83D\uDEE1 Safety \u0026amp; Validation\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth validates configuration at startup:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EInvalid combinations are rejected\u003C/li\u003E\n\u003Cli\u003EMissing required values fail fast\u003C/li\u003E\n\u003Cli\u003EUnsafe configurations are blocked\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You will not run with a broken configuration\u003C/p\u003E\n\u003Ch2 id=\u0022common-pitfalls\u0022\u003E\u26A0\uFE0F Common Pitfalls\u003C/h2\u003E\n\u003Ch3 id=\u0022assuming-code-overrides-config\u0022\u003E\u274C Assuming code overrides config\u003C/h3\u003E\n\u003Cp\u003EIt does not.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 appsettings.json always wins\u003C/p\u003E\n\u003Ch3 id=\u0022hardcoding-production-values\u0022\u003E\u274C Hardcoding production values\u003C/h3\u003E\n\u003Cp\u003EAvoid:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.Token.AccessTokenLifetime = TimeSpan.FromHours(1);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Use environment config instead for maximum flexibility\u003C/p\u003E\n\u003Ch3 id=\u0022mixing-environments-unintentionally\u0022\u003E\u274C Mixing environments unintentionally\u003C/h3\u003E\n\u003Cp\u003EEnsure correct environment is set:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003EASPNETCORE_ENVIRONMENT=Production\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Code defines defaults\u003Cbr /\u003E\n\uD83D\uDC49 Configuration defines reality\u003C/p\u003E\n\u003Ch2 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EUltimateAuth supports code \u002B configuration\u003C/li\u003E\n\u003Cli\u003Eappsettings.json overrides Program.cs\u003C/li\u003E\n\u003Cli\u003EEnvironment-based configuration is first-class\u003C/li\u003E\n\u003Cli\u003EValidation prevents unsafe setups\u003C/li\u003E\n\u003Cli\u003EDesigned for real-world deployment\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003EAdvanced Configuration\u003C/strong\u003E\u003C/p\u003E\n" +} \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/index.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/index.json new file mode 100644 index 00000000..e42cc166 --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/index.json @@ -0,0 +1,5 @@ +{ + "Slug": "configuration/index", + "Title": "Configuration", + "Html": "\n\u003Cp\u003EUltimateAuth is designed to be flexible.\u003C/p\u003E\n\u003Cp\u003EBut flexibility without structure leads to chaos.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Configuration in UltimateAuth is structured, layered, and safe by default.\u003C/p\u003E\n\u003Ch2 id=\u0022what-you-configure\u0022\u003E\uD83E\uDDE0 What You Configure\u003C/h2\u003E\n\u003Cp\u003EIn UltimateAuth, you don\u2019t just configure values.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 You configure behavior.\u003C/p\u003E\n\u003Cp\u003EThis includes:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EHow sessions are created and managed\u003C/li\u003E\n\u003Cli\u003EHow tokens are issued and refreshed\u003C/li\u003E\n\u003Cli\u003EHow tenants are resolved\u003C/li\u003E\n\u003Cli\u003EHow clients interact with the system\u003C/li\u003E\n\u003Cli\u003EWhich features are enabled or restricted\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022configuration-layers\u0022\u003E\uD83E\uDDE9 Configuration Layers\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth separates configuration into three layers:\u003C/p\u003E\n\u003Ch3 id=\u0022core\u0022\u003E\uD83D\uDD39 Core\u003C/h3\u003E\n\u003Cp\u003EDefines authentication behavior:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession lifecycle\u003C/li\u003E\n\u003Cli\u003EToken policies\u003C/li\u003E\n\u003Cli\u003EPKCE flows\u003C/li\u003E\n\u003Cli\u003EMulti-tenancy\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022server\u0022\u003E\uD83D\uDD39 Server\u003C/h3\u003E\n\u003Cp\u003EDefines runtime behavior:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAllowed authentication modes\u003C/li\u003E\n\u003Cli\u003EEndpoint exposure\u003C/li\u003E\n\u003Cli\u003ECookie and transport behavior\u003C/li\u003E\n\u003Cli\u003EHub deployment\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022client\u0022\u003E\uD83D\uDD39 Client\u003C/h3\u003E\n\u003Cp\u003EDefines client-side behavior:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EClient profile\u003C/li\u003E\n\u003Cli\u003EPKCE configuration\u003C/li\u003E\n\u003Cli\u003EAuto-refresh and re-authentication\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 These layers are independent but work together.\u003C/p\u003E\n\u003Ch2 id=\u0022configuration-sources\u0022\u003E\u2699\uFE0F Configuration Sources\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth supports two configuration styles:\u003C/p\u003E\n\u003Ch3 id=\u0022code-based-program.cs\u0022\u003ECode-based (Program.cs)\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthServer(o =\u0026gt;\n{\n o.Login.MaxFailedAttempts = 5;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022configuration-based-appsettings.json\u0022\u003EConfiguration-based (appsettings.json)\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003E{\n \u0026quot;UltimateAuth\u0026quot;: {\n \u0026quot;Server\u0026quot;: {\n \u0026quot;Login\u0026quot;: {\n \u0026quot;MaxFailedAttempts\u0026quot;: 5\n }\n }\n }\n}\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 appsettings.json overrides Program.cs\u003C/p\u003E\n\u003Cp\u003EThis allows:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEnvironment-based configuration\u003C/li\u003E\n\u003Cli\u003ECentralized management\u003C/li\u003E\n\u003Cli\u003EProduction-safe overrides\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022safety-by-design\u0022\u003E\uD83D\uDEE1 Safety by Design\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth does not allow unsafe configurations silently.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EInvalid combinations fail at startup\u003C/li\u003E\n\u003Cli\u003EUnsupported modes are rejected\u003C/li\u003E\n\u003Cli\u003ESecurity invariants are enforced\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Flexibility is allowed\n\uD83D\uDC49 Unsafe behavior is not\u003C/p\u003E\n\u003Ch2 id=\u0022whats-next\u0022\u003E\uD83C\uDFAF What\u2019s Next?\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EUnderstand configuration layers \u2192 Configuration Overview\u003C/li\u003E\n\u003Cli\u003ELearn Core behavior \u2192 Core Options\u003C/li\u003E\n\u003Cli\u003ECustomize server \u2192 Server Options\u003C/li\u003E\n\u003Cli\u003EControl clients \u2192 Client Options\u003C/li\u003E\n\u003Cli\u003EGo deeper \u2192 Advanced Configuration\u003C/li\u003E\n\u003C/ul\u003E\n" +} \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/server-options.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/server-options.json new file mode 100644 index 00000000..4152d2fa --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/server-options.json @@ -0,0 +1,5 @@ +{ + "Slug": "configuration/server-options", + "Title": "Server Options", + "Html": "\n\u003Cp\u003EUltimateAuth is configured primarily through \u003Cstrong\u003EServer Options\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 This is the main entry point for configuring authentication behavior.\u003C/p\u003E\n\u003Ch2 id=\u0022what-are-server-options\u0022\u003E\uD83E\uDDE0 What Are Server Options?\u003C/h2\u003E\n\u003Cp\u003EServer options define how UltimateAuth behaves \u003Cstrong\u003Einside your application\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EThey control:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAuthentication behavior\u003C/li\u003E\n\u003Cli\u003ESecurity policies\u003C/li\u003E\n\u003Cli\u003EToken issuance\u003C/li\u003E\n\u003Cli\u003ESession lifecycle\u003C/li\u003E\n\u003Cli\u003EEndpoint exposure\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022basic-usage\u0022\u003E\u2699\uFE0F Basic Usage\u003C/h2\u003E\n\u003Cp\u003EYou configure server options in \u003Ccode\u003EProgram.cs\u003C/code\u003E:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthServer(o =\u0026gt;\n{\n o.Login.MaxFailedAttempts = 5;\n o.Session.IdleTimeout = TimeSpan.FromDays(7);\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Chr /\u003E\n\u003Cp\u003EYou can also use \u003Ccode\u003Eappsettings.json\u003C/code\u003E:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-json\u0022\u003E{\n \u0026quot;UltimateAuth\u0026quot;: {\n \u0026quot;Server\u0026quot;: {\n \u0026quot;Login\u0026quot;: {\n \u0026quot;MaxFailedAttempts\u0026quot;: 5\n },\n \u0026quot;Session\u0026quot;: {\n \u0026quot;IdleTimeout\u0026quot;: \u0026quot;07.00.00.00\u0026quot;\n }\n }\n }\n}\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ccode\u003Eappsettings.json\u003C/code\u003E overrides \u003Ccode\u003EProgram.cs\u003C/code\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022core-composition\u0022\u003E\uD83E\uDDE9 Core Composition\u003C/h2\u003E\n\u003Cp\u003EServer options include Core behavior:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogin\u003C/li\u003E\n\u003Cli\u003ESession\u003C/li\u003E\n\u003Cli\u003EToken\u003C/li\u003E\n\u003Cli\u003EPKCE\u003C/li\u003E\n\u003Cli\u003EMulti-tenancy\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 These are defined in Core\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 But configured via Server\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022important-you-dont-configure-modes-directly\u0022\u003E\u26A0\uFE0F Important: You Don\u2019t Configure Modes Directly\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth does NOT expect you to select a single auth mode.\u003C/p\u003E\n\u003Cp\u003EInstead:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Mode is resolved at runtime\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022allowed-modes-guardrail\u0022\u003E\uD83D\uDEE1 Allowed Modes (Guardrail)\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.AllowedModes = new[]\n{\n UAuthMode.Hybrid,\n UAuthMode.PureOpaque\n};\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This does NOT select a mode\u003Cbr /\u003E\n\uD83D\uDC49 It restricts which modes are allowed\u003C/p\u003E\n\u003Cp\u003EIf a resolved mode is not allowed:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Request fails early\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022runtime-behavior-effective-options\u0022\u003E\u26A1 Runtime Behavior (Effective Options)\u003C/h2\u003E\n\u003Cp\u003EServer options are not used directly.\u003C/p\u003E\n\u003Cp\u003EThey are transformed into:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ccode\u003EEffectiveUAuthServerOptions\u003C/code\u003E\u003C/p\u003E\n\u003Cp\u003EThis happens per request:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EMode is resolved\u003C/li\u003E\n\u003Cli\u003EDefaults are applied\u003C/li\u003E\n\u003Cli\u003EOverrides are applied\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 What actually runs is \u003Cstrong\u003EEffective Options\u003C/strong\u003E\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mode-based-defaults\u0022\u003E\uD83D\uDD04 Mode-Based Defaults\u003C/h2\u003E\n\u003Cp\u003EEach auth mode applies different defaults automatically:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EPureOpaque \u2192 session-heavy\u003C/li\u003E\n\u003Cli\u003EHybrid \u2192 session \u002B token\u003C/li\u003E\n\u003Cli\u003EPureJwt \u2192 token-only\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You don\u2019t need to manually configure everything\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022endpoint-control\u0022\u003E\uD83C\uDF9B Endpoint Control\u003C/h2\u003E\n\u003Cp\u003EYou can control which features are enabled:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.Endpoints.Authentication = true;\no.Endpoints.Session = true;\no.Endpoints.Authorization = true;\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EYou can also disable specific actions:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.Endpoints.DisabledActions.Add(\u0026quot;UAuthActions.Users.Create.Anonymous\u0026quot;);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Useful for API hardening\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022cookie-transport-behavior\u0022\u003E\uD83C\uDF6A Cookie \u0026amp; Transport Behavior\u003C/h2\u003E\n\u003Cp\u003EServer options define how credentials are transported:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECookies\u003C/li\u003E\n\u003Cli\u003EHeaders\u003C/li\u003E\n\u003Cli\u003ETokens\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Unsafe combinations are rejected at startup\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022hub-configuration\u0022\u003E\uD83C\uDF10 Hub Configuration\u003C/h2\u003E\n\u003Cp\u003EIf using UAuthHub:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.HubDeploymentMode = UAuthHubDeploymentMode.Integrated;\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Defines how auth server is deployed\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022session-resolution\u0022\u003E\uD83D\uDD01 Session Resolution\u003C/h2\u003E\n\u003Cp\u003EControls how session IDs are extracted:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECookie\u003C/li\u003E\n\u003Cli\u003EHeader\u003C/li\u003E\n\u003Cli\u003EBearer\u003C/li\u003E\n\u003Cli\u003EQuery\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Fully configurable\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Server options define \u003Cstrong\u003Ewhat is allowed\u003C/strong\u003E\u003Cbr /\u003E\n\uD83D\uDC49 Runtime determines \u003Cstrong\u003Ewhat is used\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EServer options are the main configuration entry\u003C/li\u003E\n\u003Cli\u003ECore behavior is configured via server\u003C/li\u003E\n\u003Cli\u003EModes are not selected manually\u003C/li\u003E\n\u003Cli\u003EEffective options are computed per request\u003C/li\u003E\n\u003Cli\u003ESecurity is enforced by design\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Ch2 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003EClient Options\u003C/strong\u003E\u003C/p\u003E\n" +} \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/docs-index.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/docs-index.json new file mode 100644 index 00000000..2216ca65 --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/docs-index.json @@ -0,0 +1,282 @@ +[ + { + "Title": "Getting Started", + "Slug": "getting-started/index", + "Order": 1, + "Group": "getting-started", + "GroupOrder": 1 + }, + { + "Title": "QuickStart", + "Slug": "getting-started/quickstart", + "Order": 2, + "Group": "getting-started", + "GroupOrder": 1 + }, + { + "Title": "Real World Setup", + "Slug": "getting-started/real-world-setup", + "Order": 3, + "Group": "getting-started", + "GroupOrder": 1 + }, + { + "Title": "Authentication Model", + "Slug": "fundamentals/auth-model", + "Order": 2, + "Group": "fundamentals", + "GroupOrder": 2 + }, + { + "Title": "Flow Based Authentication", + "Slug": "fundamentals/flow-based-auth", + "Order": 3, + "Group": "fundamentals", + "GroupOrder": 2 + }, + { + "Title": "Authentication Modes", + "Slug": "fundamentals/auth-modes", + "Order": 4, + "Group": "fundamentals", + "GroupOrder": 2 + }, + { + "Title": "Client Profiles", + "Slug": "fundamentals/client-profiles", + "Order": 5, + "Group": "fundamentals", + "GroupOrder": 2 + }, + { + "Title": "Runtime Architecture", + "Slug": "fundamentals/runtime-architecture", + "Order": 6, + "Group": "fundamentals", + "GroupOrder": 2 + }, + { + "Title": "Request Lifecycle", + "Slug": "fundamentals/request-lifecycle", + "Order": 7, + "Group": "fundamentals", + "GroupOrder": 2 + }, + { + "Title": "Auth Flows", + "Slug": "auth-flows/index", + "Order": 1, + "Group": "auth-flows", + "GroupOrder": 3 + }, + { + "Title": "Login", + "Slug": "auth-flows/login-flow", + "Order": 2, + "Group": "auth-flows", + "GroupOrder": 3 + }, + { + "Title": "Refresh", + "Slug": "auth-flows/refresh-flow", + "Order": 3, + "Group": "auth-flows", + "GroupOrder": 3 + }, + { + "Title": "Logout", + "Slug": "auth-flows/logout-flow", + "Order": 4, + "Group": "auth-flows", + "GroupOrder": 3 + }, + { + "Title": "Session Lifecycle", + "Slug": "auth-flows/session-lifecycle", + "Order": 5, + "Group": "auth-flows", + "GroupOrder": 3 + }, + { + "Title": "Token Behavior", + "Slug": "auth-flows/token-behavior", + "Order": 6, + "Group": "auth-flows", + "GroupOrder": 3 + }, + { + "Title": "Device Management", + "Slug": "auth-flows/device-management", + "Order": 7, + "Group": "auth-flows", + "GroupOrder": 3 + }, + { + "Title": "Plugin Domains", + "Slug": "plugin-domains/index", + "Order": 1, + "Group": "plugin-domains", + "GroupOrder": 4 + }, + { + "Title": "Users", + "Slug": "plugin-domains/users-domain", + "Order": 2, + "Group": "plugin-domains", + "GroupOrder": 4 + }, + { + "Title": "Credentials", + "Slug": "plugin-domains/credential-domain", + "Order": 3, + "Group": "plugin-domains", + "GroupOrder": 4 + }, + { + "Title": "Authorization", + "Slug": "plugin-domains/authorization-domain", + "Order": 4, + "Group": "plugin-domains", + "GroupOrder": 4 + }, + { + "Title": "Policies", + "Slug": "plugin-domains/policies", + "Order": 5, + "Group": "plugin-domains", + "GroupOrder": 4 + }, + { + "Title": "Client \u0026 API", + "Slug": "client/index", + "Order": 1, + "Group": "client", + "GroupOrder": 5 + }, + { + "Title": "Authentication", + "Slug": "client/authentication", + "Order": 2, + "Group": "client", + "GroupOrder": 5 + }, + { + "Title": "Session Management", + "Slug": "client/session-management", + "Order": 3, + "Group": "client", + "GroupOrder": 5 + }, + { + "Title": "User Management", + "Slug": "client/user-management", + "Order": 4, + "Group": "client", + "GroupOrder": 5 + }, + { + "Title": "Identifiers", + "Slug": "client/identifiers", + "Order": 5, + "Group": "client", + "GroupOrder": 5 + }, + { + "Title": "Credentials", + "Slug": "client/credentials", + "Order": 6, + "Group": "client", + "GroupOrder": 5 + }, + { + "Title": "Authorization", + "Slug": "client/authorization", + "Order": 7, + "Group": "client", + "GroupOrder": 5 + }, + { + "Title": "Configuration", + "Slug": "configuration/index", + "Order": 1, + "Group": "configuration", + "GroupOrder": 6 + }, + { + "Title": "Configuration Overview", + "Slug": "configuration/configuration-overview", + "Order": 2, + "Group": "configuration", + "GroupOrder": 6 + }, + { + "Title": "Server Options", + "Slug": "configuration/server-options", + "Order": 3, + "Group": "configuration", + "GroupOrder": 6 + }, + { + "Title": "Client Options", + "Slug": "configuration/client-options", + "Order": 4, + "Group": "configuration", + "GroupOrder": 6 + }, + { + "Title": "Configuration Sources", + "Slug": "configuration/configuration-sources", + "Order": 5, + "Group": "configuration", + "GroupOrder": 6 + }, + { + "Title": "Advanced Configuration", + "Slug": "configuration/advanced-configuration", + "Order": 6, + "Group": "configuration", + "GroupOrder": 6 + }, + { + "Title": "Session Security Model", + "Slug": "security/session-security-model", + "Order": 1, + "Group": "security", + "GroupOrder": 7 + }, + { + "Title": "Access Token Behavior", + "Slug": "security/access-token-behavior", + "Order": 2, + "Group": "security", + "GroupOrder": 7 + }, + { + "Title": "Refresh Rotation", + "Slug": "security/refresh-rotation", + "Order": 3, + "Group": "security", + "GroupOrder": 7 + }, + { + "Title": "Policy Pipeline", + "Slug": "security/policy-pipeline", + "Order": 4, + "Group": "security", + "GroupOrder": 7 + }, + { + "Title": "\uD83D\uDD10 UltimateAuth Docs", + "Slug": "readme", + "Order": 999, + "Group": "", + "GroupOrder": 999 + }, + { + "Title": "index", + "Slug": "fundamentals/index", + "Order": 999, + "Group": "", + "GroupOrder": 999 + } +] \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/auth-model.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/auth-model.json new file mode 100644 index 00000000..df5cbbf6 --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/auth-model.json @@ -0,0 +1,5 @@ +{ + "Slug": "fundamentals/auth-model", + "Title": "Authentication Model", + "Html": "\n\u003Cp\u003EUltimateAuth is built around a simple but powerful idea:\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003EAuthentication is not just a token.\u003Cbr /\u003E\nIt is a \u003Cstrong\u003Estructured, server-controlled session model\u003C/strong\u003E.\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cp\u003EAt the core of this model are three concepts:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Cstrong\u003ERoot\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003EChain\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003ESession\u003C/strong\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003ETogether, they form what we call:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Cstrong\u003EAuthentication Lineage\u003C/strong\u003E\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022the-big-picture\u0022\u003E\uD83D\uDD11 The Big Picture\u003C/h2\u003E\n\u003Cp\u003EInstead of treating authentication as a single token or cookie, UltimateAuth models it as a hierarchy:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003ERoot (user authority)\n\u251C\u2500\u2500 Chain (device context)\n\u2502 \u251C\u2500\u2500 Session (login instance)\n\u2502 \u251C\u2500\u2500 Session\n\u2502\n\u251C\u2500\u2500 Chain\n\u2502 \u251C\u2500\u2500 Session (login instance)\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EEach level has a distinct responsibility.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022root-the-authority\u0022\u003E\uD83E\uDDE9 Root \u2014 The Authority\u003C/h2\u003E\n\u003Cp\u003E\u003Cstrong\u003ERoot\u003C/strong\u003E represents the authentication authority of a user within a tenant.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EThere is \u003Cstrong\u003Eonly one active Root per user per tenant\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EIt defines the \u003Cstrong\u003Eglobal security state\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EIt controls all chains and sessions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022what-root-does\u0022\u003EWhat Root Does\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ETracks security version (security epoch)\u003C/li\u003E\n\u003Cli\u003EInvalidates all sessions when needed\u003C/li\u003E\n\u003Cli\u003EActs as the \u003Cstrong\u003Esource of truth\u003C/strong\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022example\u0022\u003EExample\u003C/h3\u003E\n\u003Cp\u003EIf a user changes their password:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Root is updated\u003Cbr /\u003E\n\uD83D\uDC49 All existing sessions can become invalid\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022chain-the-device-context\u0022\u003E\uD83D\uDD17 Chain \u2014 The Device Context\u003C/h2\u003E\n\u003Cp\u003E\u003Cstrong\u003EChain\u003C/strong\u003E represents a device or client context.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEach device typically has its own chain\u003C/li\u003E\n\u003Cli\u003EMultiple logins from the same device belong to the same chain\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Think of Chain as:\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003E\u201CWhere is the user logged in from?\u201D\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Ch3 id=\u0022what-chain-does\u0022\u003EWhat Chain Does\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Cp\u003EGroups sessions by device\u003C/p\u003E\n\u003C/li\u003E\n\u003Cli\u003E\u003Cp\u003EEnables \u003Cstrong\u003Edevice-level control\u003C/strong\u003E\u003C/p\u003E\n\u003C/li\u003E\n\u003Cli\u003E\u003Cp\u003EAllows actions like:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogout from one device\u003C/li\u003E\n\u003Cli\u003ERevoke a specific device\u003C/li\u003E\n\u003C/ul\u003E\n\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022session-the-authentication-instance\u0022\u003E\uD83E\uDDFE Session \u2014 The Authentication Instance\u003C/h2\u003E\n\u003Cp\u003E\u003Cstrong\u003ESession\u003C/strong\u003E is the actual authentication instance.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECreated when the user logs in\u003C/li\u003E\n\u003Cli\u003ERepresents a single authenticated state\u003C/li\u003E\n\u003Cli\u003ECarries a snapshot of the Root security version\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This is what gets validated on each request.\u003C/p\u003E\n\u003Ch3 id=\u0022what-session-does\u0022\u003EWhat Session Does\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EProves the user is authenticated\u003C/li\u003E\n\u003Cli\u003ECan be refreshed, revoked, or expired\u003C/li\u003E\n\u003Cli\u003EIs tied to a specific chain\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022how-they-work-together\u0022\u003E\uD83D\uDD04 How They Work Together\u003C/h2\u003E\n\u003Cp\u003EWhen a user logs in:\u003C/p\u003E\n\u003Col\u003E\n\u003Cli\u003ERoot is resolved (or created)\u003C/li\u003E\n\u003Cli\u003EA Chain is identified (device context)\u003C/li\u003E\n\u003Cli\u003EA new Session is created\u003C/li\u003E\n\u003C/ol\u003E\n\u003Cp\u003E\u003Ccode\u003ELogin \u2192 Root \u2192 Chain \u2192 Session\u003C/code\u003E\u003C/p\u003E\n\u003Cp\u003EOn each request:\u003C/p\u003E\n\u003Col\u003E\n\u003Cli\u003ESession is validated\u003C/li\u003E\n\u003Cli\u003EChain context is checked\u003C/li\u003E\n\u003Cli\u003ERoot security version is verified\u003C/li\u003E\n\u003C/ol\u003E\n\u003Cp\u003E\uD83D\uDC49 If any level is invalid \u2192 authentication fails\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022why-this-model-matters\u0022\u003E\uD83D\uDEE1 Why This Model Matters\u003C/h2\u003E\n\u003Cp\u003ETraditional systems rely on:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECookies\u003C/li\u003E\n\u003Cli\u003EJWT tokens\u003C/li\u003E\n\u003Cli\u003EStateless validation\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThese approaches have limitations:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ENo real session control\u003C/li\u003E\n\u003Cli\u003EWeak revocation\u003C/li\u003E\n\u003Cli\u003ENo device awareness\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Cp\u003EUltimateAuth solves this by:\u003C/p\u003E\n\u003Ch3 id=\u0022server-controlled-authentication\u0022\u003E\u2714 Server-Controlled Authentication\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ESessions are always validated server-side\u003C/li\u003E\n\u003Cli\u003ENo blind trust in tokens\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022instant-revocation\u0022\u003E\u2714 Instant Revocation\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ERevoke a session \u2192 immediate effect\u003C/li\u003E\n\u003Cli\u003ERevoke a chain \u2192 device logged out\u003C/li\u003E\n\u003Cli\u003ERevoke root \u2192 disable user (global logout)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022device-awareness\u0022\u003E\u2714 Device Awareness\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EEach device has its own chain\u003C/li\u003E\n\u003Cli\u003ESessions are bound to context\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022strong-security-model\u0022\u003E\u2714 Strong Security Model\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession carries Root security version\u003C/li\u003E\n\u003Cli\u003EOld sessions automatically become invalid\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember only one thing, remember this:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Cstrong\u003ERoot = authority\u003C/strong\u003E\u003Cbr /\u003E\n\uD83D\uDC49 \u003Cstrong\u003EChain = device\u003C/strong\u003E\u003Cbr /\u003E\n\uD83D\uDC49 \u003Cstrong\u003ESession = login\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EAuthentication is not just a token\u003C/li\u003E\n\u003Cli\u003ESessions are first-class citizens\u003C/li\u003E\n\u003Cli\u003EThe server always remains in control\u003C/li\u003E\n\u003Cli\u003EDevice and security context are built-in\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003ENow that you understand the core model:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Continue to \u003Cstrong\u003EFlow-Based Authentication\u003C/strong\u003E\u003C/p\u003E\n" +} \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/auth-modes.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/auth-modes.json new file mode 100644 index 00000000..ad9f4466 --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/auth-modes.json @@ -0,0 +1,5 @@ +{ + "Slug": "fundamentals/auth-modes", + "Title": "Authentication Modes", + "Html": "\u003Cblockquote\u003E\n\u003Cp\u003ENote: SemiHybrid and PureJwt modes will be available on future releases. For now you can safely use PureOpaque and Hybrid modes.\u003C/p\u003E\n\u003C/blockquote\u003E\n\n\u003Cp\u003EUltimateAuth supports multiple authentication modes.\u003C/p\u003E\n\u003Cp\u003EEach mode represents a different balance between:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESecurity\u003C/li\u003E\n\u003Cli\u003EPerformance\u003C/li\u003E\n\u003Cli\u003EControl\u003C/li\u003E\n\u003Cli\u003EClient capabilities\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You don\u2019t always choose a single model.\u003Cbr /\u003E\nUltimateAuth can adapt based on context.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022available-modes\u0022\u003E\uD83E\uDDE9 Available Modes\u003C/h2\u003E\n\u003Ch3 id=\u0022pureopaque\u0022\u003EPureOpaque\u003C/h3\u003E\n\u003Cp\u003EFully server-controlled session model.\u003C/p\u003E\n\u003Ch3 id=\u0022hybrid\u0022\u003EHybrid\u003C/h3\u003E\n\u003Cp\u003ECombines session control with token-based access.\u003C/p\u003E\n\u003Ch3 id=\u0022semihybrid\u0022\u003ESemiHybrid\u003C/h3\u003E\n\u003Cp\u003EJWT-based with server-side metadata awareness.\u003C/p\u003E\n\u003Ch3 id=\u0022purejwt\u0022\u003EPureJwt\u003C/h3\u003E\n\u003Cp\u003EFully stateless token-based authentication.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mode-comparison\u0022\u003E\u2696\uFE0F Mode Comparison\u003C/h2\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EFeature\u003C/th\u003E\n\u003Cth\u003EPureOpaque\u003C/th\u003E\n\u003Cth\u003EHybrid\u003C/th\u003E\n\u003Cth\u003ESemiHybrid\u003C/th\u003E\n\u003Cth\u003EPureJwt\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ESessionId\u003C/td\u003E\n\u003Ctd\u003ERequired\u003C/td\u003E\n\u003Ctd\u003ERequired\u003C/td\u003E\n\u003Ctd\u003EMetadata only\u003C/td\u003E\n\u003Ctd\u003ENone\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EAccess Token\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ERefresh Token\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ERevocation\u003C/td\u003E\n\u003Ctd\u003EImmediate\u003C/td\u003E\n\u003Ctd\u003EImmediate\u003C/td\u003E\n\u003Ctd\u003EMetadata-based\u003C/td\u003E\n\u003Ctd\u003ENot immediate\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EStatefulness\u003C/td\u003E\n\u003Ctd\u003EFull\u003C/td\u003E\n\u003Ctd\u003EHybrid\u003C/td\u003E\n\u003Ctd\u003ESemi\u003C/td\u003E\n\u003Ctd\u003EStateless\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EServer Control\u003C/td\u003E\n\u003Ctd\u003EFull\u003C/td\u003E\n\u003Ctd\u003EHigh\u003C/td\u003E\n\u003Ctd\u003EMedium\u003C/td\u003E\n\u003Ctd\u003ELow\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EPerformance*\u003C/td\u003E\n\u003Ctd\u003EMedium\u003C/td\u003E\n\u003Ctd\u003EMedium\u003C/td\u003E\n\u003Ctd\u003EHigh\u003C/td\u003E\n\u003Ctd\u003EHighest\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EOffline Support\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003EPartial\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003E\u26A1 \u003Cstrong\u003EPerformance Note\u003C/strong\u003E\u003C/p\u003E\n\u003Cp\u003EAll modes in UltimateAuth are designed for production use and are highly optimized.\u003C/p\u003E\n\u003Cp\u003EThe differences here are about \u003Cstrong\u003Etrade-offs\u003C/strong\u003E, not absolute speed:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Even the most server-controlled mode is performant enough for real-world applications.\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022how-to-think-about-auth-modes\u0022\u003E\uD83E\uDDE0 How to Think About Auth Modes\u003C/h2\u003E\n\u003Cp\u003EIt\u2019s important to understand that authentication modes in UltimateAuth are not rigid system-wide choices.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 You are not expected to pick a single mode and use it everywhere.\u003C/p\u003E\n\u003Cp\u003EInstead:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EDifferent clients can use different modes on a single UAuthHub\u003C/li\u003E\n\u003Cli\u003EThe mode can change \u003Cstrong\u003Eper request\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EUltimateAuth selects the most appropriate mode based on \u003Cstrong\u003EClient Profile and runtime context\u003C/strong\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch3 id=\u0022runtime-driven-behavior\u0022\u003E\uD83D\uDD04 Runtime-Driven Behavior\u003C/h3\u003E\n\u003Cp\u003EIn a typical application:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EBlazor Server \u2192 PureOpaque\u003C/li\u003E\n\u003Cli\u003EBlazor WASM \u2192 Hybrid\u003C/li\u003E\n\u003Cli\u003EAPI \u2192 PureJwt\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 All can coexist in the same system.\u003C/p\u003E\n\u003Cp\u003EYou don\u2019t split your architecture \u2014 UltimateAuth adapts automatically.\u003C/p\u003E\n\u003Ch3 id=\u0022you-can-override-everything\u0022\u003E\u2699\uFE0F You Can Override Everything\u003C/h3\u003E\n\u003Cp\u003EUltimateAuth provides \u003Cstrong\u003Esafe defaults\u003C/strong\u003E, but nothing is locked.\u003C/p\u003E\n\u003Cp\u003EYou can:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EForce a specific auth mode\u003C/li\u003E\n\u003Cli\u003ECustomize behavior per client\u003C/li\u003E\n\u003Cli\u003EReplace default strategies\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 The system is designed to be flexible without sacrificing safety.\u003C/p\u003E\n\u003Ch3 id=\u0022safe-by-default\u0022\u003E\uD83D\uDEE1 Safe by Default\u003C/h3\u003E\n\u003Cp\u003EThe comparison table shows trade-offs \u2014 not risks.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAll modes are \u003Cstrong\u003Evalid and supported\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EChoosing a different mode will not \u201Cbreak\u201D security\u003C/li\u003E\n\u003Cli\u003EIncompatible configurations will \u003Cstrong\u003Efail fast\u003C/strong\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You are always operating within a safe boundary.\u003C/p\u003E\n\u003Ch3 id=\u0022mental-model\u0022\u003E\uD83D\uDCA1 Mental Model\u003C/h3\u003E\n\u003Cp\u003EThink of auth modes as:\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003EDifferent execution strategies \u2014 not different systems.\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cp\u003EUltimateAuth remains consistent.\u003Cbr /\u003E\nOnly the \u003Cstrong\u003Ebehavior adapts\u003C/strong\u003E.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022pureopaque-1\u0022\u003E\uD83D\uDD10 PureOpaque\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EFully session-based\u003C/li\u003E\n\u003Cli\u003EEvery request validated on server\u003C/li\u003E\n\u003Cli\u003EMaximum security\u003C/li\u003E\n\u003Cli\u003ETouch semantics instead of refresh rotation\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Best for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EBlazor Server\u003C/li\u003E\n\u003Cli\u003EInternal apps\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022hybrid-1\u0022\u003E\u26A1 Hybrid\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EAccess token as opaque session id\u003C/li\u003E\n\u003Cli\u003ERefresh token with rotate semantics\u003C/li\u003E\n\u003Cli\u003EServer control with API performance\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Best for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EBlazor WASM\u003C/li\u003E\n\u003Cli\u003EWeb \u002B API systems\u003C/li\u003E\n\u003Cli\u003EFull-stack apps\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022semihybrid-1\u0022\u003E\uD83D\uDE80 SemiHybrid\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EJWT-based access\u003C/li\u003E\n\u003Cli\u003EServer-side metadata control\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Best for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EHigh-performance APIs\u003C/li\u003E\n\u003Cli\u003EZero-trust systems\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022purejwt-1\u0022\u003E\uD83C\uDF10 PureJwt\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EFully stateless\u003C/li\u003E\n\u003Cli\u003ENo server-side session control\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Best for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EExternal APIs\u003C/li\u003E\n\u003Cli\u003EMicroservices\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022which-mode-should-you-use\u0022\u003E\uD83C\uDFAF Which Mode Should You Use?\u003C/h2\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EScenario\u003C/th\u003E\n\u003Cth\u003ERecommended Mode\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EBlazor Server\u003C/td\u003E\n\u003Ctd\u003EPureOpaque\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EWeb \u002B API\u003C/td\u003E\n\u003Ctd\u003EHybrid\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EHigh-scale API\u003C/td\u003E\n\u003Ctd\u003ESemiHybrid\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EExternal microservices\u003C/td\u003E\n\u003Ctd\u003EPureJwt\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n" +} \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/client-profiles.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/client-profiles.json new file mode 100644 index 00000000..ccaf4764 --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/client-profiles.json @@ -0,0 +1,5 @@ +{ + "Slug": "fundamentals/client-profiles", + "Title": "Client Profiles", + "Html": "\n\u003Cp\u003EUltimateAuth adapts its authentication behavior based on the client.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 This is powered by \u003Cstrong\u003EClient Profiles\u003C/strong\u003E.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022what-is-a-client-profile\u0022\u003E\uD83D\uDD11 What is a Client Profile?\u003C/h2\u003E\n\u003Cp\u003EA Client Profile defines how authentication behaves for a specific client type.\u003C/p\u003E\n\u003Cp\u003EIt affects:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAuthentication mode\u003C/li\u003E\n\u003Cli\u003EFlow behavior\u003C/li\u003E\n\u003Cli\u003EToken usage\u003C/li\u003E\n\u003Cli\u003ESecurity constraints\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EUnlike traditional systems:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 You don\u2019t configure authentication globally\u003Cbr /\u003E\n\uD83D\uDC49 You let the system adapt per client\u003C/p\u003E\n\u003Ch2 id=\u0022the-key-idea\u0022\u003E\uD83E\uDDE0 The Key Idea\u003C/h2\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003EAuthentication behavior is not static\u003Cbr /\u003E\nIt is determined \u003Cstrong\u003Eper client and per request\u003C/strong\u003E\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022runtime-detection\u0022\u003E\uD83D\uDD04 Runtime Detection\u003C/h2\u003E\n\u003Cp\u003EBy default, UltimateAuth automatically detects the client profile.\u003C/p\u003E\n\u003Cp\u003EThis is done using runtime signals such as:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELoaded assemblies\u003C/li\u003E\n\u003Cli\u003EHosting environment\u003C/li\u003E\n\u003Cli\u003ERegistered services\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022example-detection-logic\u0022\u003EExample Detection Logic\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EMAUI assemblies \u2192 \u003Ccode\u003EMaui\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003EWebAssembly runtime \u2192 \u003Ccode\u003EBlazorWasm\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003EServer components \u2192 \u003Ccode\u003EBlazorServer\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003EUAuthHub marker \u2192 \u003Ccode\u003EUAuthHub\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003EFallback \u2192 \u003Ccode\u003EWebServer\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Detection happens inside the client at startup.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022client-server-propagation\u0022\u003E\uD83D\uDCE1 Client \u2192 Server Propagation\u003C/h2\u003E\n\u003Cp\u003EThe detected client profile is sent to the server on each request.\u003C/p\u003E\n\u003Cp\u003EThis can happen via:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERequest headers\u003C/li\u003E\n\u003Cli\u003EForm fields (for flow-based operations)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EClient \u2192 (ClientProfile) \u2192 Server\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EOn the server:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EThe profile is read from the request\u003C/li\u003E\n\u003Cli\u003EIf not provided \u2192 NotSpecified\u003C/li\u003E\n\u003Cli\u003EServer applies defaults or resolves behavior\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022automatic-vs-manual-configuration\u0022\u003E\u2699\uFE0F Automatic vs Manual Configuration\u003C/h2\u003E\n\u003Ch3 id=\u0022automatic-default\u0022\u003EAutomatic (Default)\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthClientBlazor();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EIt means:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAutoDetectClientProfile = true\u003C/li\u003E\n\u003Cli\u003EProfile is resolved automatically\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022manual-override\u0022\u003EManual Override\u003C/h3\u003E\n\u003Cp\u003EYou can explicitly set the client profile:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthClientBlazor(o =\u0026gt;\n{\n o.ClientProfile = UAuthClientProfile.Maui;\n o.AutoDetectClientProfile = false; // optional\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This is useful when:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERunning in custom hosting environments\u003C/li\u003E\n\u003Cli\u003EDetection is ambiguous\u003C/li\u003E\n\u003Cli\u003EYou want full control\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022built-in-profiles\u0022\u003E\uD83E\uDDE9 Built-in Profiles\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth includes predefined profiles:\u003C/p\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EProfile\u003C/th\u003E\n\u003Cth\u003EDescription\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EBlazorServer\u003C/td\u003E\n\u003Ctd\u003EServer-rendered apps\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EBlazorWasm\u003C/td\u003E\n\u003Ctd\u003EBrowser-based WASM apps\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EMaui\u003C/td\u003E\n\u003Ctd\u003ENative mobile apps\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EWebServer\u003C/td\u003E\n\u003Ctd\u003EMVC / Razor / generic server\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EApi\u003C/td\u003E\n\u003Ctd\u003EAPI-only backend\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EUAuthHub\u003C/td\u003E\n\u003Ctd\u003ECentral auth server\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Ch3 id=\u0022safe-defaults\u0022\u003E\uD83D\uDEE1 Safe Defaults\u003C/h3\u003E\n\u003Cp\u003EIf no profile is specified (and auto detection is false):\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EClient \u2192 NotSpecified \u2192 Server resolves safely\u003C/li\u003E\n\u003Cli\u003EDefaults are applied\u003C/li\u003E\n\u003Cli\u003EUnsafe combinations are prevented\u003C/li\u003E\n\u003Cli\u003ESystem remains consistent\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022why-this-matters\u0022\u003E\uD83D\uDD10 Why This Matters\u003C/h3\u003E\n\u003Cp\u003EClient Profiles enable:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EMulti-client systems (Web \u002B Mobile \u002B API)\u003C/li\u003E\n\u003Cli\u003ERuntime adaptation\u003C/li\u003E\n\u003Cli\u003ESafe defaults without manual configuration\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EWithout Client Profiles You would need:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESeparate auth setups per client\u003C/li\u003E\n\u003Cli\u003EComplex branching logic\u003C/li\u003E\n\u003Cli\u003EManual security handling\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Client defines behavior\n\uD83D\uDC49 Server enforces rules\u003C/p\u003E\n\u003Ch2 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EClient Profiles are automatically detected\u003C/li\u003E\n\u003Cli\u003EThey are sent to the server on each request\u003C/li\u003E\n\u003Cli\u003EBehavior adapts per request\u003C/li\u003E\n\u003Cli\u003EYou can override everything when needed\u003C/li\u003E\n\u003Cli\u003ESafe defaults are always applied\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003ENow that you understand runtime behavior:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Continue to Runtime Architecture\u003C/p\u003E\n" +} \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/flow-based-auth.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/flow-based-auth.json new file mode 100644 index 00000000..f9b15a79 --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/flow-based-auth.json @@ -0,0 +1,5 @@ +{ + "Slug": "fundamentals/flow-based-auth", + "Title": "Flow Based Authentication", + "Html": "\n\u003Cp\u003EUltimateAuth is not cookie-based or token-based.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 It is \u003Cstrong\u003Eflow-based\u003C/strong\u003E.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022what-does-flow-based-mean\u0022\u003E\uD83D\uDD11 What Does \u201CFlow-Based\u201D Mean?\u003C/h2\u003E\n\u003Cp\u003EIn traditional systems, authentication is treated as:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EA cookie\u003C/li\u003E\n\u003Cli\u003EA JWT token\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EOnce issued, the system simply checks:\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003E\u201CIs this token valid?\u201D\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cbr\u003E\n\u003Cp\u003EUltimateAuth takes a different approach:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Authentication is a \u003Cstrong\u003Eseries of controlled flows\u003C/strong\u003E, not a static artifact.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022authentication-as-flows\u0022\u003E\uD83E\uDDED Authentication as Flows\u003C/h2\u003E\n\u003Cp\u003EEvery authentication operation is an explicit flow:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Cstrong\u003ELogin\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003ELogout\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003EValidate\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003ERefresh\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003ERe-authentication\u003C/strong\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EEach flow:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EIs initiated intentionally\u003C/li\u003E\n\u003Cli\u003EIs processed on the server\u003C/li\u003E\n\u003Cli\u003EProduces a controlled result\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022example-login-flow\u0022\u003E\uD83D\uDD01 Example: Login Flow\u003C/h2\u003E\n\u003Cp\u003EInstead of:\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003E\u201CGenerate a token and store it\u201D\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cp\u003EUltimateAuth does:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003ELogin Request\n\u2192 Validate credentials\n\u2192 Resolve Root\n\u2192 Resolve or create Chain\n\u2192 Create Session\n\u2192 Issue authentication grant\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Login is not a single step \u2014 it is a \u003Cstrong\u003Emanaged process\u003C/strong\u003E\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022example-refresh-flow\u0022\u003E\uD83D\uDD04 Example: Refresh Flow\u003C/h2\u003E\n\u003Cp\u003ETraditional systems:\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003ERefresh = issue new token\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cp\u003EUltimateAuth:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003ERefresh Request\n\u2192 Validate session\n\u2192 Check security constraints\n\u2192 Apply policies (if any)\n\u2192 Optionally rotate tokens\n\u2192 Update session state (if needed)\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 The server decides what actually happens\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022example-validate-flow\u0022\u003E\uD83D\uDD0D Example: Validate Flow\u003C/h2\u003E\n\u003Cp\u003EOn each request:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003EIncoming Request\n\u2192 Extract session/token\n\u2192 Validate session\n\u2192 Check chain (device context)\n\u2192 Verify root security version\n\u2192 Build auth state\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Validation is not just \u201Ctoken valid?\u201D\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022why-token-based-thinking-falls-short\u0022\u003E\u26A0\uFE0F Why Token-Based Thinking Falls Short\u003C/h2\u003E\n\u003Cp\u003EToken-based systems assume:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EThe token contains truth\u003C/li\u003E\n\u003Cli\u003EThe server trusts the token\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThis leads to:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EWeak revocation\u003C/li\u003E\n\u003Cli\u003ENo device awareness\u003C/li\u003E\n\u003Cli\u003ELimited control\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022ultimateauth-approach\u0022\u003E\u2705 UltimateAuth Approach\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth treats tokens as:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Cstrong\u003Etransport artifacts\u003C/strong\u003E, not sources of truth\u003C/p\u003E\n\u003Cp\u003EThe real authority is:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERoot\u003C/li\u003E\n\u003Cli\u003EChain\u003C/li\u003E\n\u003Cli\u003ESession\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022key-idea\u0022\u003E\uD83E\uDDE0 Key Idea\u003C/h2\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003ETokens carry data\u003Cbr /\u003E\nFlows enforce rules\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022server-controlled-by-design\u0022\u003E\uD83D\uDD10 Server-Controlled by Design\u003C/h2\u003E\n\u003Cp\u003EAll flows are:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EExecuted on the server\u003C/li\u003E\n\u003Cli\u003EEvaluated against policies\u003C/li\u003E\n\u003Cli\u003ESubject to security constraints\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 The client does not control authentication state\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022flow-examples-in-code\u0022\u003E\u2699\uFE0F Flow Examples in Code\u003C/h2\u003E\n\u003Cp\u003EUsing \u003Ccode\u003EIUAuthClient\u003C/code\u003E:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Flows.LoginAsync(request);\nawait UAuthClient.Flows.RefreshAsync();\nawait UAuthClient.Flows.LogoutAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Each method represents a server-driven flow\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022how-this-changes-development\u0022\u003E\uD83E\uDDE9 How This Changes Development\u003C/h2\u003E\n\u003Cp\u003EInstead of thinking:\u003C/p\u003E\n\u003Cp\u003E\u274C \u201CI need to manage tokens\u201D\u003C/p\u003E\n\u003Cp\u003EYou think:\u003C/p\u003E\n\u003Cp\u003E\u2705 \u201CI need to trigger flows\u201D\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022benefits-of-flow-based-authentication\u0022\u003E\uD83D\uDCCC Benefits of Flow-Based Authentication\u003C/h2\u003E\n\u003Ch3 id=\u0022predictable-behavior\u0022\u003E\u2714 Predictable Behavior\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EEvery action is explicit and controlled.\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022better-security\u0022\u003E\u2714 Better Security\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ENo blind token trust\u003C/li\u003E\n\u003Cli\u003EServer-side validation\u003C/li\u003E\n\u003Cli\u003EPolicy-driven decisions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022extensibility\u0022\u003E\u2714 Extensibility\u003C/h3\u003E\n\u003Cp\u003EFlows can be extended with:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EMFA\u003C/li\u003E\n\u003Cli\u003ERisk-based checks\u003C/li\u003E\n\u003Cli\u003ECustom policies\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022consistent-across-clients\u0022\u003E\u2714 Consistent Across Clients\u003C/h3\u003E\n\u003Cp\u003ESame flows work for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EBlazor Server\u003C/li\u003E\n\u003Cli\u003EWASM (PKCE)\u003C/li\u003E\n\u003Cli\u003EAPIs\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Authentication is not a token \u2014 it is a process\u003C/p\u003E\n\u003Ch2 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003ENow that you understand flows:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Continue to Auth Modes\u003C/p\u003E\n" +} \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/index.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/index.json new file mode 100644 index 00000000..e09e0b9e --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/index.json @@ -0,0 +1,5 @@ +{ + "Slug": "fundamentals/index", + "Title": "index", + "Html": "\u003Cp\u003E---\u003C/p\u003E\n\u003Cp\u003Etitle: Fundamental Overview\u003C/p\u003E\n\u003Cp\u003Eorder: 1\u003C/p\u003E\n\u003Cp\u003Egroup: fundamentals\u003C/p\u003E\n\u003Cp\u003E---\u003C/p\u003E\n\u003Cp\u003E# \uD83E\uDDE0 Fundamentals\u003C/p\u003E\n\u003Cp\u003EThis section explains how UltimateAuth works internally.\u003C/p\u003E\n\u003Cp\u003EIf you are new, follow this order:\u003C/p\u003E\n\u003Cp\u003E- Authentication Model\u003C/p\u003E\n\u003Cp\u003E- Flow-Based Authentication\u003C/p\u003E\n\u003Cp\u003E- Authentication Modes\u003C/p\u003E\n\u003Cp\u003E- Client Profiles\u003C/p\u003E\n\u003Cp\u003E- Runtime Architecture\u003C/p\u003E\n\u003Cp\u003E- Request Lifecycle\u003C/p\u003E\n" +} \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/request-lifecycle.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/request-lifecycle.json new file mode 100644 index 00000000..548b498a --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/request-lifecycle.json @@ -0,0 +1,5 @@ +{ + "Slug": "fundamentals/request-lifecycle", + "Title": "Request Lifecycle", + "Html": "\n\u003Cp\u003EThis section explains what happens when a request enters UltimateAuth.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 From the moment an HTTP request arrives\u003Cbr /\u003E\n\uD83D\uDC49 Until authentication state is established or a flow is executed\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022two-types-of-requests\u0022\u003E\uD83E\uDDE0 Two Types of Requests\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth processes requests in two different ways:\u003C/p\u003E\n\u003Ch3 id=\u0022passive-requests\u0022\u003E1. Passive Requests\u003C/h3\u003E\n\u003Cp\u003ERegular application requests (page load, API call)\u003C/p\u003E\n\u003Ch3 id=\u0022active-flow-requests\u0022\u003E2. Active Flow Requests\u003C/h3\u003E\n\u003Cp\u003EAuthentication flows (login, refresh, logout)\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Both share the same foundation, but diverge at the flow level.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022middleware-pipeline\u0022\u003E\uD83E\uDDE9 Middleware Pipeline\u003C/h2\u003E\n\u003Cp\u003EEvery request passes through the UltimateAuth middleware pipeline:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003ETenant \u2192 Session Resolution \u2192 (Validation) \u2192 User Resolution\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022tenant-resolution\u0022\u003E\uD83C\uDFE2 Tenant Resolution\u003C/h3\u003E\n\u003Cp\u003EThe system determines the tenant:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EMulti-tenant \u2192 resolved via \u003Ccode\u003EITenantResolver\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003ESingle-tenant \u2192 default context applied\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 If tenant cannot be resolved \u2192 request fails early\u003C/p\u003E\n\u003Ch3 id=\u0022session-resolution\u0022\u003E\uD83D\uDD10 Session Resolution\u003C/h3\u003E\n\u003Cp\u003EThe system attempts to extract a session:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EFrom headers, cookies, or tokens\u003C/li\u003E\n\u003Cli\u003EConverted into a \u003Ccode\u003ESessionContext\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cpre\u003E\u003Ccode\u003ESessionId \u2192 SessionContext\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 If no session is found \u2192 request becomes anonymous\u003C/p\u003E\n\u003Ch3 id=\u0022session-validation-resource-apis\u0022\u003E\u2714 Session Validation (Resource APIs)\u003C/h3\u003E\n\u003Cp\u003EFor API scenarios:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession is validated immediately\u003C/li\u003E\n\u003Cli\u003EDevice context is considered\u003C/li\u003E\n\u003Cli\u003EA validation result is attached to the request\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This enables stateless or semi-stateful validation\u003C/p\u003E\n\u003Ch3 id=\u0022user-resolution\u0022\u003E\uD83D\uDC64 User Resolution\u003C/h3\u003E\n\u003Cp\u003EThe system resolves the current user:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EBased on validated session\u003C/li\u003E\n\u003Cli\u003EUsing \u003Ccode\u003EIUserAccessor\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This produces the final user context\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022passive-request-flow\u0022\u003E\uD83D\uDD04 Passive Request Flow\u003C/h2\u003E\n\u003Cp\u003EFor normal requests:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003ERequest \n\u2192 Middleware pipeline \n\u2192 Session resolved \n\u2192 User resolved \n\u2192 Application executes \n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 No flow execution happens\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022active-flow-requests-1\u0022\u003E\uD83D\uDD10 Active Flow Requests\u003C/h2\u003E\n\u003Cp\u003EFor auth endpoints (login, refresh, etc.):\u003C/p\u003E\n\u003Cp\u003EThe lifecycle continues beyond middleware.\u003C/p\u003E\n\u003Ch3 id=\u0022step-1-flow-detection\u0022\u003EStep 1: Flow Detection\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode\u003EEndpoint \u2192 FlowType\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022step-2-context-creation\u0022\u003EStep 2: Context Creation\u003C/h3\u003E\n\u003Cp\u003EAn \u003Ccode\u003EAuthFlowContext\u003C/code\u003E is created.\u003C/p\u003E\n\u003Cp\u003EIt includes:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EClient profile\u003C/li\u003E\n\u003Cli\u003EEffective mode\u003C/li\u003E\n\u003Cli\u003ETenant\u003C/li\u003E\n\u003Cli\u003EDevice\u003C/li\u003E\n\u003Cli\u003ESession\u003C/li\u003E\n\u003Cli\u003EResponse configuration\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This defines the execution environment\u003C/p\u003E\n\u003Ch3 id=\u0022step-3-flow-execution\u0022\u003EStep 3: Flow Execution\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode\u003EAuthFlowContext \u2192 Flow Service \u2192 Orchestrator \u2192 Authority\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022step-4-state-mutation\u0022\u003EStep 4: State Mutation\u003C/h3\u003E\n\u003Cp\u003EDepending on the flow:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession may be created, updated, or revoked\u003C/li\u003E\n\u003Cli\u003ETokens may be issued\u003C/li\u003E\n\u003Cli\u003ESecurity state may change\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022step-5-response-generation\u0022\u003EStep 5: Response Generation\u003C/h3\u003E\n\u003Cp\u003EThe system writes the response:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESessionId\u003C/li\u003E\n\u003Cli\u003EAccess token\u003C/li\u003E\n\u003Cli\u003ERefresh token\u003C/li\u003E\n\u003Cli\u003ERedirect (if needed)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022example-login-request\u0022\u003E\uD83D\uDD01 Example: Login Request\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode\u003EHTTP Request \n\u2192 Tenant resolved \n\u2192 Session resolved (anonymous) \n\u2192 Flow detected (Login) \n\u2192 AuthFlowContext created \n\u2192 Credentials validated \n\u2192 Session created \n\u2192 Tokens issued \n\u2192 Response returned \n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022flow-execution-boundary\u0022\u003E\uD83D\uDD10 Flow Execution Boundary\u003C/h2\u003E\n\u003Cp\u003EAuthentication flows are only executed for endpoints explicitly marked with flow metadata.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERegular requests do not create an AuthFlowContext\u003C/li\u003E\n\u003Cli\u003EAuthFlowContext is only created during flow execution\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This ensures that authentication logic does not interfere with normal application behavior.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003E\uD83D\uDC49 Middleware prepares the request\u003Cbr /\u003E\n\uD83D\uDC49 Flows change the state\u003C/p\u003E\n" +} \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/runtime-architecture.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/runtime-architecture.json new file mode 100644 index 00000000..3f442ef3 --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/runtime-architecture.json @@ -0,0 +1,5 @@ +{ + "Slug": "fundamentals/runtime-architecture", + "Title": "Runtime Architecture", + "Html": "\n\u003Cp\u003EUltimateAuth processes authentication through a structured execution pipeline.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 It is not just middleware-based authentication\u003Cbr /\u003E\n\uD83D\uDC49 It is a \u003Cstrong\u003Eflow-driven execution system\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022the-big-picture\u0022\u003E\uD83E\uDDE0 The Big Picture\u003C/h2\u003E\n\u003Cp\u003EWhen a request reaches an auth endpoint:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ERequest\n \u2192 Endpoint Filter\n \u2192 AuthFlowContext\n \u2192 Endpoint Handler\n \u2192 Flow Service\n \u2192 Orchestrator\n \u2192 Authority\n \u2192 Stores / Issuers\n \u2192 Response\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EEach step has a clearly defined responsibility.\u003C/p\u003E\n\u003Ch2 id=\u0022request-entry-point\u0022\u003E\uD83D\uDD04 Request Entry Point\u003C/h2\u003E\n\u003Cp\u003EAuthentication begins at the endpoint level.\u003C/p\u003E\n\u003Cp\u003EAn endpoint filter detects the flow:\u003C/p\u003E\n\u003Cp\u003E\u003Ccode\u003EEndpoint \u2192 FlowType (Login, Refresh, Logout\u2026)\u003C/code\u003E\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 The system knows which flow is being executed before any logic runs.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022authflowcontext-the-core-state\u0022\u003E\uD83E\uDDFE AuthFlowContext \u2014 The Core State\u003C/h2\u003E\n\u003Cp\u003EBefore any operation starts, UltimateAuth creates an AuthFlowContext.\u003C/p\u003E\n\u003Cp\u003EThis is the central object that carries:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EClient profile\u003C/li\u003E\n\u003Cli\u003EEffective authentication mode\u003C/li\u003E\n\u003Cli\u003ETenant information\u003C/li\u003E\n\u003Cli\u003EDevice context\u003C/li\u003E\n\u003Cli\u003ESession state\u003C/li\u003E\n\u003Cli\u003EResponse configuration\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This context defines the entire execution environment\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022flow-service-entry-layer\u0022\u003E\u2699\uFE0F Flow Service \u2014 Entry Layer\u003C/h2\u003E\n\u003Cp\u003EAfter the context is created, the request is passed to the Flow Service.\u003C/p\u003E\n\u003Cp\u003EThe Flow Service:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EActs as the entry point for all flows\u003C/li\u003E\n\u003Cli\u003ENormalizes execution\u003C/li\u003E\n\u003Cli\u003EDelegates work to orchestrators\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 It does not implement business logic directly\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022orchestrator-flow-coordinator\u0022\u003E\uD83E\uDDED Orchestrator \u2014 Flow Coordinator\u003C/h2\u003E\n\u003Cp\u003EThe Orchestrator manages the execution of a flow.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECoordinates multiple steps\u003C/li\u003E\n\u003Cli\u003EEnsures correct execution order\u003C/li\u003E\n\u003Cli\u003EDelegates decisions to Authority\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Think of it as the flow engine\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022authority-decision-layer\u0022\u003E\uD83D\uDD10 Authority \u2014 Decision Layer\u003C/h2\u003E\n\u003Cp\u003EThe Authority is the most critical component.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EValidates authentication state\u003C/li\u003E\n\u003Cli\u003EApplies security rules\u003C/li\u003E\n\u003Cli\u003EApproves or rejects operations\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 No sensitive operation bypasses Authority\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022services-stores\u0022\u003E\u2699\uFE0F Services \u0026amp; Stores\u003C/h2\u003E\n\u003Cp\u003EOnce decisions are made:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EServices handle domain logic\u003C/li\u003E\n\u003Cli\u003EStores handle persistence\u003C/li\u003E\n\u003Cli\u003EIssuers generate tokens or session artifacts\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 These layers do not make security decisions\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022end-to-end-example-login\u0022\u003E\uD83D\uDD01 End-to-End Example (Login)\u003C/h2\u003E\n\u003Cp\u003ELogin Request\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003E \u2192 Endpoint Filter (Login Flow)\n \u2192 AuthFlowContext created\n \u2192 Flow Service\n \u2192 Orchestrator\n \u2192 Authority validates credentials\n \u2192 Session created (Store)\n \u2192 Tokens issued (Issuer)\n \u2192 Response generated\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022why-this-architecture-matters\u0022\u003E\uD83E\uDDE0 Why This Architecture Matters\u003C/h2\u003E\n\u003Cp\u003E\u2714 Centralized Decision Making\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAuthority is always in control\u003C/li\u003E\n\u003Cli\u003ENo scattered validation logic\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\u2714 Predictable Execution\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEvery flow follows the same pipeline\u003C/li\u003E\n\u003Cli\u003ENo hidden behavior\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\u2714 Extensibility\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EReplace stores\u003C/li\u003E\n\u003Cli\u003EExtend flows\u003C/li\u003E\n\u003Cli\u003ECustomize orchestration\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\u2714 Security by Design\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ENo bypass of Authority\u003C/li\u003E\n\u003Cli\u003EContext-driven validation\u003C/li\u003E\n\u003Cli\u003EFlow-aware execution\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022relation-to-other-concepts\u0022\u003E\uD83D\uDD17 Relation to Other Concepts\u003C/h2\u003E\n\u003Cp\u003EThis architecture connects all previous concepts:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAuth Model (Root / Chain / Session) \u2192 validated in Authority\u003C/li\u003E\n\u003Cli\u003EAuth Flows \u2192 executed by Orchestrator\u003C/li\u003E\n\u003Cli\u003EAuth Modes \u2192 applied via EffectiveMode\u003C/li\u003E\n\u003Cli\u003EClient Profiles \u2192 influence behavior at runtime\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Flow defines what happens\n\uD83D\uDC49 Context defines how it happens\n\uD83D\uDC49 Authority decides if it happens\u003C/p\u003E\n\u003Ch2 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cp\u003EAuthentication is executed as a pipeline\nAuthFlowContext carries execution state\nOrchestrator coordinates flows\nAuthority enforces security\nServices and Stores execute operations\u003C/p\u003E\n\u003Ch2 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003ENow that you understand the execution model:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Continue to Request Lifecycle\u003C/p\u003E\n" +} \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/index.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/index.json new file mode 100644 index 00000000..3031305c --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/index.json @@ -0,0 +1,5 @@ +{ + "Slug": "getting-started/index", + "Title": "Getting Started", + "Html": "\n\u003Cp\u003EWelcome to \u003Cstrong\u003EUltimateAuth\u003C/strong\u003E \u2014 the modern authentication framework for .NET.\u003C/p\u003E\n\u003Cp\u003EUltimateAuth is designed to make authentication \u003Cstrong\u003Esimple to use\u003C/strong\u003E, while still being \u003Cstrong\u003Epowerful, flexible, and deeply secure\u003C/strong\u003E enough for real-world applications.\u003C/p\u003E\n\u003Ch2 id=\u0022what-is-ultimateauth\u0022\u003EWhat is UltimateAuth?\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth is a \u003Cstrong\u003Eflow-driven authentication framework\u003C/strong\u003E that reimagines how authentication works in modern .NET applications.\u003C/p\u003E\n\u003Cp\u003EIt unifies:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession-based authentication\u003C/li\u003E\n\u003Cli\u003EToken-based authentication (JWT)\u003C/li\u003E\n\u003Cli\u003EPKCE flows for public clients\u003C/li\u003E\n\u003Cli\u003EMulti-client environments (Blazor Server, WASM, MAUI, APIs)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003Einto a single, consistent system.\u003C/p\u003E\n\u003Cp\u003EInstead of choosing between cookies, sessions, or tokens, UltimateAuth allows you to use \u003Cstrong\u003Ethe right model for each scenario \u2014 automatically\u003C/strong\u003E.\u003C/p\u003E\n\u003Ch2 id=\u0022what-makes-ultimateauth-different\u0022\u003EWhat Makes UltimateAuth Different?\u003C/h2\u003E\n\u003Ch3 id=\u0022session-is-a-first-class-concept\u0022\u003E\uD83D\uDD39 Session is a First-Class Concept\u003C/h3\u003E\n\u003Cp\u003EUnlike traditional systems, UltimateAuth treats sessions as a \u003Cstrong\u003Ecore domain\u003C/strong\u003E, not a side effect.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERoot \u2192 global security authority\u003C/li\u003E\n\u003Cli\u003EChain \u2192 device context\u003C/li\u003E\n\u003Cli\u003ESession \u2192 actual authentication instance\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThis allows:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EInstant revocation\u003C/li\u003E\n\u003Cli\u003EMulti-device control\u003C/li\u003E\n\u003Cli\u003ESecure session lifecycle management\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022flow-based-not-token-based\u0022\u003E\uD83D\uDD39 Flow-Based, Not Token-Based\u003C/h3\u003E\n\u003Cp\u003EUltimateAuth is not cookie-based or token-based.\u003C/p\u003E\n\u003Cp\u003EIt is \u003Cstrong\u003Eflow-based\u003C/strong\u003E:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogin is a flow\u003C/li\u003E\n\u003Cli\u003ERefresh is a flow\u003C/li\u003E\n\u003Cli\u003ERe-authentication is a flow\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Authentication becomes \u003Cstrong\u003Eexplicit, predictable, and controllable\u003C/strong\u003E\u003C/p\u003E\n\u003Ch3 id=\u0022built-for-blazor-and-modern-clients\u0022\u003E\uD83D\uDD39 Built for Blazor and Modern Clients\u003C/h3\u003E\n\u003Cp\u003EUltimateAuth is designed from the ground up for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EBlazor Server\u003C/li\u003E\n\u003Cli\u003EBlazor WASM\u003C/li\u003E\n\u003Cli\u003E.NET MAUI\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EWith:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ENative PKCE support\u003C/li\u003E\n\u003Cli\u003EBuilt-in Blazor components (\u003Ccode\u003EUAuthLoginForm\u003C/code\u003E, \u003Ccode\u003EUAuthApp\u003C/code\u003E)\u003C/li\u003E\n\u003Cli\u003EAutomatic client profile detection\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 No hacks. No manual glue code.\u003C/p\u003E\n\u003Ch3 id=\u0022runtime-aware-authentication\u0022\u003E\uD83D\uDD39 Runtime-Aware Authentication\u003C/h3\u003E\n\u003Cp\u003EAuthentication behavior is not static.\u003C/p\u003E\n\u003Cp\u003EUltimateAuth adapts based on:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EClient type\u003C/li\u003E\n\u003Cli\u003EAuth mode\u003C/li\u003E\n\u003Cli\u003ERequest context\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Same system, different optimized behavior.\u003C/p\u003E\n\u003Ch2 id=\u0022what-problems-it-solves\u0022\u003EWhat Problems It Solves\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth addresses real-world challenges:\u003C/p\u003E\n\u003Ch3 id=\u0022multiple-client-types\u0022\u003E\uD83D\uDD39 Multiple Client Types\u003C/h3\u003E\n\u003Cp\u003EBlazor Server, WASM, MAUI, and APIs all behave differently.\u003C/p\u003E\n\u003Cp\u003EUltimateAuth handles these differences automatically using \u003Cstrong\u003EClient Profiles\u003C/strong\u003E.\u003C/p\u003E\n\u003Ch3 id=\u0022session-vs-token-confusion\u0022\u003E\uD83D\uDD39 Session vs Token Confusion\u003C/h3\u003E\n\u003Cp\u003EShould you use cookies, sessions, or JWT?\u003C/p\u003E\n\u003Cp\u003EUltimateAuth removes this decision by supporting multiple auth modes and selecting the correct behavior at runtime.\u003C/p\u003E\n\u003Ch3 id=\u0022secure-session-management\u0022\u003E\uD83D\uDD39 Secure Session Management\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EDevice-aware sessions\u003C/li\u003E\n\u003Cli\u003ESession revocation\u003C/li\u003E\n\u003Cli\u003ERefresh token rotation\u003C/li\u003E\n\u003Cli\u003EReplay protection\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EAll built-in \u2014 no custom implementation required.\u003C/p\u003E\n\u003Ch3 id=\u0022complex-auth-flows\u0022\u003E\uD83D\uDD39 Complex Auth Flows\u003C/h3\u003E\n\u003Cp\u003ELogin, logout, refresh, PKCE, multi-device control etc.\u003C/p\u003E\n\u003Cp\u003EAll exposed as \u003Cstrong\u003Esimple application-level APIs\u003C/strong\u003E.\u003C/p\u003E\n\u003Ch2 id=\u0022when-to-use-ultimateauth\u0022\u003EWhen to Use UltimateAuth\u003C/h2\u003E\n\u003Cp\u003EUse UltimateAuth if:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EYou are building a modern .NET application \u003Cstrong\u003EBlazor Server, WASM or MAUI\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EYou need \u003Cstrong\u003Esession \u002B token hybrid authentication\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EYou want \u003Cstrong\u003Efull control over authentication flows\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EYou are building a \u003Cstrong\u003Emulti-client system (web \u002B mobile \u002B API)\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EYou need \u003Cstrong\u003Estrong security with extensibility\u003C/strong\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Continue to \u003Cstrong\u003EQuick Start\u003C/strong\u003E to build your first UltimateAuth application.\u003C/p\u003E\n" +} \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/quickstart.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/quickstart.json new file mode 100644 index 00000000..b74fa2b9 --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/quickstart.json @@ -0,0 +1,5 @@ +{ + "Slug": "getting-started/quickstart", + "Title": "QuickStart", + "Html": "\n\u003Cp\u003EIn this guide, you will set up UltimateAuth in a few minutes and perform your \u003Cstrong\u003Efirst login\u003C/strong\u003E.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch2 id=\u0022create-a-project\u0022\u003E1. Create a Project\u003C/h2\u003E\n\u003Cp\u003ECreate a new Blazor Server web app:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-bash\u0022\u003Edotnet new blazorserver -n UltimateAuthDemo\ncd UltimateAuthDemo\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022install-packages\u0022\u003E2. Install Packages\u003C/h2\u003E\n\u003Cp\u003EAdd UltimateAuth packages:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Edotnet add package CodeBeam.UltimateAuth.Server\ndotnet add package CodeBeam.UltimateAuth.Client.Blazor\ndotnet add package CodeBeam.UltimateAuth.InMemory.Bundle\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022configure-services\u0022\u003E3. Configure Services\u003C/h2\u003E\n\u003Cp\u003EUpdate your Program.cs:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services\n .AddUltimateAuthServer()\n .AddUltimateAuthInMemory();\n\nbuilder.Services\n .AddUltimateAuthClientBlazor();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022configure-middleware\u0022\u003E4. Configure Middleware\u003C/h2\u003E\n\u003Cp\u003EIn \u003Ccode\u003EProgram.cs\u003C/code\u003E\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eapp.UseUltimateAuthWithAspNetCore();\napp.MapUltimateAuthEndpoints();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022enable-blazor-integration\u0022\u003E5. Enable Blazor Integration\u003C/h2\u003E\n\u003Cp\u003EIn \u003Ccode\u003EProgram.cs\u003C/code\u003E\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eapp.MapRazorComponents\u0026lt;App\u0026gt;()\n .AddInteractiveServerRenderMode() // or webassembly (depends on your application type)\n .AddUltimateAuthRoutes(UAuthAssemblies.BlazorClient());\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022add-uauth-script\u0022\u003E6. Add UAuth Script\u003C/h2\u003E\n\u003Cp\u003EAdd this to \u003Ccode\u003EApp.razor\u003C/code\u003E or \u003Ccode\u003Eindex.html\u003C/code\u003E:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003E\u0026lt;script src=\u0026quot;_content/CodeBeam.UltimateAuth.Client.Blazor/uauth.min.js\u0026quot;\u0026gt;\u0026lt;/script\u0026gt;\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022configure-application-lifecycle\u0022\u003E7. Configure Application Lifecycle\u003C/h2\u003E\n\u003Cp\u003EReplace \u003Ccode\u003ERoutes.razor\u003C/code\u003E with this code:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003E\u0026lt;UAuthApp UseBuiltInRouter=\u0026quot;true\u0026quot; AppAssembly=\u0026quot;typeof(Program).Assembly\u0026quot; DefaultLayout=\u0026quot;typeof(Layout.MainLayout)\u0026quot; /\u0026gt;\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022perform-your-first-login\u0022\u003E8. Perform Your First Login\u003C/h2\u003E\n\u003Cp\u003EExample using IUAuthClient:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003E[Inject] IUAuthClient UAuthClient { get; set; } = null!;\n\nprivate async Task Login()\n{\n await UAuthClient.Flows.LoginAsync(new LoginRequest\n {\n Identifier = \u0026quot;demo\u0026quot;,\n Secret = \u0026quot;password\u0026quot;\n });\n}\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022thats-it\u0022\u003E\uD83C\uDF89 That\u2019s It\u003C/h2\u003E\n\u003Cp\u003EYou now have a working authentication system with:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession-based authentication\u003C/li\u003E\n\u003Cli\u003EAutomatic client detection\u003C/li\u003E\n\u003Cli\u003EBuilt-in login flow\u003C/li\u003E\n\u003Cli\u003ESecure session handling\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022what-just-happened\u0022\u003EWhat Just Happened?\u003C/h2\u003E\n\u003Cp\u003EWhen you logged in:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EA session (with root and chain) was created on the server,\u003C/li\u003E\n\u003Cli\u003EYour client received an authentication grant (cookie or token),\u003C/li\u003E\n\u003Cli\u003EUltimateAuth established your auth state automatically.\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You didn\u2019t manage cookies, tokens, or redirects manually.\u003C/p\u003E\n\u003Ch2 id=\u0022next-steps\u0022\u003ENext Steps\u003C/h2\u003E\n\u003Cp\u003EDiscover the setup for real world applications with entity framework core.\u003C/p\u003E\n" +} \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/real-world-setup.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/real-world-setup.json new file mode 100644 index 00000000..7ff27001 --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/real-world-setup.json @@ -0,0 +1,5 @@ +{ + "Slug": "getting-started/real-world-setup", + "Title": "Real World Setup", + "Html": "\n\u003Cp\u003EThe Quick Start uses an in-memory setup for simplicity.\nIn real-world applications, you should replace it with a persistent configuration as shown below.\u003C/p\u003E\n\u003Cp\u003EIn real applications, you will typically configure:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EA persistent database\u003C/li\u003E\n\u003Cli\u003EAn appropriate client profile\u003C/li\u003E\n\u003Cli\u003EA suitable authentication mode\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThis guide shows how to set up UltimateAuth for real-world scenarios.\u003C/p\u003E\n\u003Ch2 id=\u0022using-entity-framework-core\u0022\u003E\uD83D\uDDC4\uFE0F Using Entity Framework Core\u003C/h2\u003E\n\u003Cp\u003EFor production, you should use a persistent store. In this setup, you no longer need the \u003Ccode\u003ECodeBeam.UltimateAuth.InMemory.Bundle\u003C/code\u003E package.\u003C/p\u003E\n\u003Ch3 id=\u0022install-packages\u0022\u003EInstall Packages\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-bash\u0022\u003Edotnet add package CodeBeam.UltimateAuth.EntityFrameworkCore.Bundle\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022configure-services\u0022\u003EConfigure Services\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services\n .AddUltimateAuthServer()\n .AddUltimateAuthEntityFrameworkCore(db =\u0026gt;\n {\n db.UseSqlite(\u0026quot;Data Source=uauth.db\u0026quot;);\n // or UseSqlServer / UseNpgsql\n });\n\nbuilder.Services\n .AddUltimateAuthClientBlazor();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022create-database-migrations\u0022\u003ECreate Database \u0026amp; Migrations\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-bash\u0022\u003Edotnet ef migrations add InitUAuth\ndotnet ef database update\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003Eor\u003C/p\u003E\n\u003Cp\u003EIf you are using Visual Studio, you can run these commands in Package Manager Console*:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-bash\u0022\u003EAdd-Migration InitUAuth -Context UAuthDbContext\nUpdate-Database -Context UAuthDbContext\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E*Needs \u003Ccode\u003EMicrosoft.EntityFrameworkCore.Design\u003C/code\u003E and \u003Ccode\u003EMicrosoft.EntityFrameworkCore.Tools\u003C/code\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022configure-services-with-options\u0022\u003EConfigure Services With Options\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth provides rich options for server and client service registration.\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthServer(o =\u0026gt; {\n o.Diagnostics.EnableRefreshDetails = true;\n o.Login.MaxFailedAttempts = 4;\n o.Identifiers.AllowMultipleUsernames = true;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022blazor-wasm-setup\u0022\u003EBlazor WASM Setup\u003C/h2\u003E\n\u003Cp\u003EBlazor WASM applications run entirely on the client and cannot securely handle credentials.\nFor this reason, UltimateAuth uses a dedicated Auth server called \u003Cstrong\u003EUAuthHub\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EWASM \u003Ccode\u003EProgram.cs\u003C/code\u003E:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthClientBlazor(o =\u0026gt;\n{\n o.Endpoints.BasePath = \u0026quot;https://localhost:6110/auth\u0026quot;; // UAuthHub URL\n o.Pkce.ReturnUrl = \u0026quot;https://localhost:6130/home\u0026quot;; // Your (WASM) application domain \u002B return path\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EUAuthHub \u003Ccode\u003EProgram.cs\u003C/code\u003E:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthServer()\n .AddUltimateAuthInMemory()\n .AddUAuthHub(o =\u0026gt; o.AllowedClientOrigins.Add(\u0026quot;https://localhost:6130\u0026quot;)); // WASM application\u0027s URL\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EUAuthHub Pipeline Configuration\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eapp.MapUltimateAuthEndpoints();\napp.MapUAuthHub();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003E\u2139\uFE0F UltimateAuth automatically selects the appropriate authentication mode (PureOpaque, Hybrid, etc.) based on the client type.\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Ch2 id=\u0022resourceapi-setup\u0022\u003EResourceApi Setup\u003C/h2\u003E\n\u003Cp\u003EYou may want to secure your custom API with UltimateAuth. UltimateAuth provides a lightweight option for this case. (ResourceApi doesn\u0027t have to be a blazor application, it can be any server-side project like MVC.)\u003C/p\u003E\n\u003Cp\u003EResourceApi\u0027s \u003Ccode\u003EProgram.cs\u003C/code\u003E\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthResourceApi(o =\u0026gt;\n {\n o.UAuthHubBaseUrl = \u0026quot;https://localhost:6110\u0026quot;;\n o.AllowedClientOrigins.Add(\u0026quot;https://localhost:6130\u0026quot;);\n });\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EConfigure pipeline:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eapp.UseUltimateAuthResourceApiWithAspNetCore();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003ENotes:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EResourceApi should connect with an UAuthHub, not a pure-server. Make sure \u003Ccode\u003E.AddUAuthHub()\u003C/code\u003E after calling \u003Ccode\u003Ebuilder.Services.AddUltimateAuthServer()\u003C/code\u003E.\u003C/li\u003E\n\u003Cli\u003EUltimateAuth automatically configures CORS based on the provided origins.\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EUse ResourceApi when:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EYou have a separate backend API\u003C/li\u003E\n\u003Cli\u003EYou want to validate sessions or tokens externally\u003C/li\u003E\n\u003Cli\u003EYour API is not hosting UltimateAuth directly\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022how-to-think-about-setup\u0022\u003E\uD83E\uDDE0 How to Think About Setup\u003C/h2\u003E\n\u003Cp\u003EIn UltimateAuth:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EThe \u003Cstrong\u003EServer\u003C/strong\u003E manages authentication flows and sessions\u003C/li\u003E\n\u003Cli\u003EThe \u003Cstrong\u003EClient\u003C/strong\u003E interacts through flows (not tokens directly)\u003C/li\u003E\n\u003Cli\u003EThe \u003Cstrong\u003EStorage layer\u003C/strong\u003E (InMemory / EF Core) defines persistence\u003C/li\u003E\n\u003Cli\u003EThe \u003Cstrong\u003EApplication type\u003C/strong\u003E determines runtime behavior\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You configure the system once, and UltimateAuth adapts automatically.\u003C/p\u003E\n" +} \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/authorization-domain.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/authorization-domain.json new file mode 100644 index 00000000..2c8562a0 --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/authorization-domain.json @@ -0,0 +1,5 @@ +{ + "Slug": "plugin-domains/authorization-domain", + "Title": "Authorization", + "Html": "\n\u003Cp\u003EUltimateAuth provides a flexible and extensible authorization system based on:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERoles\u003C/li\u003E\n\u003Cli\u003EPermissions\u003C/li\u003E\n\u003Cli\u003EPolicies\u003C/li\u003E\n\u003Cli\u003EAccess orchestration\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022core-concepts\u0022\u003E\uD83E\uDDE9 Core Concepts\u003C/h2\u003E\n\u003Ch3 id=\u0022permissions\u0022\u003E\uD83D\uDD11 Permissions\u003C/h3\u003E\n\u003Cp\u003EIn UltimateAuth, permissions are not just arbitrary strings.\u003C/p\u003E\n\u003Cp\u003EThey follow a \u003Cstrong\u003Estructured action model\u003C/strong\u003E.\u003C/p\u003E\n\u003Ch4 id=\u0022permission-structure\u0022\u003E\uD83E\uDDE9 Permission Structure\u003C/h4\u003E\n\u003Cp\u003EPermissions are built using a consistent format:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003Eresource.operation.scope\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003Eor\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003Eresource.subresource.operation.scope\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch4 id=\u0022examples\u0022\u003E\u2705 Examples\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003Eusers.create.admin\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003Eusers.profile.update.self\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003Esessions.revokechain.admin\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003Ecredentials.change.self\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This structure is not accidental \u2014\u003Cbr /\u003E\nit is \u003Cstrong\u003Edesigned for consistency, readability, and policy evaluation\u003C/strong\u003E.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch3 id=\u0022built-in-action-catalog\u0022\u003E\u2699\uFE0F Built-in Action Catalog\u003C/h3\u003E\n\u003Cp\u003EUltimateAuth provides a predefined action catalog.\u003C/p\u003E\n\u003Cp\u003EExamples:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003Eflows.logout.self\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003Esessions.listchains.admin\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003Eusers.delete.self\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003Ecredentials.revoke.admin\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003Eauthorization.roles.assign.admin\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This ensures:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ENo magic strings\u003C/li\u003E\n\u003Cli\u003EDiscoverable permissions\u003C/li\u003E\n\u003Cli\u003EConsistent naming across the system\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch3 id=\u0022scope-semantics\u0022\u003E\uD83E\uDDE0 Scope Semantics\u003C/h3\u003E\n\u003Cp\u003EThe last part of the permission defines \u003Cstrong\u003Escope\u003C/strong\u003E:\u003C/p\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EScope\u003C/th\u003E\n\u003Cth\u003EMeaning\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Ccode\u003Eself\u003C/code\u003E\u003C/td\u003E\n\u003Ctd\u003EUser acts on own resources\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Ccode\u003Eadmin\u003C/code\u003E\u003C/td\u003E\n\u003Ctd\u003EUser acts on other users\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Ccode\u003Eanonymous\u003C/code\u003E\u003C/td\u003E\n\u003Ctd\u003ENo authentication required\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Cbr\u003E\n\u003Ch3 id=\u0022wildcards-grouping\u0022\u003E\uD83C\uDF32 Wildcards \u0026amp; Grouping\u003C/h3\u003E\n\u003Cp\u003EPermissions support hierarchical matching:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003Eusers.*\u003C/code\u003E \u2192 all user actions\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003Eusers.profile.*\u003C/code\u003E \u2192 all profile operations\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003E*\u003C/code\u003E \u2192 full access\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022normalization\u0022\u003E\u26A1 Normalization\u003C/h3\u003E\n\u003Cp\u003EPermissions are automatically normalized:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EFull coverage \u2192 replaced with \u003Ccode\u003E*\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003EFull group \u2192 replaced with \u003Ccode\u003Eprefix.*\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch3 id=\u0022role\u0022\u003ERole\u003C/h3\u003E\n\u003Cp\u003EA role is a collection of permissions.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERoles are tenant-scoped\u003C/li\u003E\n\u003Cli\u003ERoles can be dynamically updated\u003C/li\u003E\n\u003Cli\u003EPermissions are normalized internally\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022userrole\u0022\u003EUserRole\u003C/h3\u003E\n\u003Cp\u003EUsers are assigned roles:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EMany-to-many relationship\u003C/li\u003E\n\u003Cli\u003EAssignment is timestamped\u003C/li\u003E\n\u003Cli\u003ERole resolution is runtime-based\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022permission-resolution\u0022\u003E\uD83D\uDD04 Permission Resolution\u003C/h2\u003E\n\u003Cp\u003EPermissions are evaluated using:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EExact match\u003C/li\u003E\n\u003Cli\u003EPrefix match\u003C/li\u003E\n\u003Cli\u003EWildcard match\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003ECompiledPermissionSet optimizes runtime checks.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022claims-integration\u0022\u003E\uD83E\uDDE0 Claims Integration\u003C/h2\u003E\n\u003Cp\u003EAuthorization integrates with authentication via claims:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERoles \u2192 \u003Ccode\u003EClaimTypes.Role\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003EPermissions \u2192 \u003Ccode\u003Epermission\u003C/code\u003E claim\u003C/li\u003E\n\u003Cli\u003ETenant \u2192 \u003Ccode\u003Etenant\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThis allows:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EToken-based authorization\u003C/li\u003E\n\u003Cli\u003EStateless permission checks (for JWT modes)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022authorization-flow\u0022\u003E\u2699\uFE0F Authorization Flow\u003C/h2\u003E\n\u003Cp\u003EAuthorization is executed through:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 AccessOrchestrator\u003C/p\u003E\n\u003Cp\u003ESteps:\u003C/p\u003E\n\u003Col\u003E\n\u003Cli\u003EBuild AccessContext\u003C/li\u003E\n\u003Cli\u003EExecute policies\u003C/li\u003E\n\u003Cli\u003EAllow or deny operation\u003C/li\u003E\n\u003C/ol\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022policies\u0022\u003E\uD83D\uDEE1 Policies\u003C/h2\u003E\n\u003Cp\u003EPolicies are the core of authorization logic.\u003C/p\u003E\n\u003Cp\u003EDefault policies include:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERequireAuthenticated\u003C/li\u003E\n\u003Cli\u003EDenyCrossTenant\u003C/li\u003E\n\u003Cli\u003ERequireActiveUser\u003C/li\u003E\n\u003Cli\u003ERequireSelf\u003C/li\u003E\n\u003Cli\u003ERequireSystem\u003C/li\u003E\n\u003Cli\u003EMustHavePermission\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022plugin-integration\u0022\u003E\uD83D\uDD0C Plugin Integration\u003C/h2\u003E\n\u003Cp\u003EAuthorization is a plugin domain.\u003C/p\u003E\n\u003Cp\u003EIt:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EDoes NOT depend on other domains\u003C/li\u003E\n\u003Cli\u003EUses contracts only\u003C/li\u003E\n\u003Cli\u003EIntegrates via policies and claims\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022key-takeaways\u0022\u003E\uD83C\uDFAF Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EAuthorization is policy-driven\u003C/li\u003E\n\u003Cli\u003ERoles are permission containers\u003C/li\u003E\n\u003Cli\u003EPermissions support wildcard \u0026amp; prefix\u003C/li\u003E\n\u003Cli\u003EPolicies enforce rules\u003C/li\u003E\n\u003Cli\u003EFully extensible and replaceable\u003C/li\u003E\n\u003C/ul\u003E\n" +} \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/credential-domain.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/credential-domain.json new file mode 100644 index 00000000..13e24a26 --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/credential-domain.json @@ -0,0 +1,5 @@ +{ + "Slug": "plugin-domains/credential-domain", + "Title": "Credentials", + "Html": "\n\u003Cp\u003ECredentials in UltimateAuth define how a user proves their identity.\u003C/p\u003E\n\u003Ch2 id=\u0022core-concept\u0022\u003E\uD83E\uDDE0 Core Concept\u003C/h2\u003E\n\u003Cp\u003EAuthentication is not tied to users directly.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 It is performed through credentials.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022what-is-a-credential\u0022\u003E\uD83D\uDD11 What is a Credential?\u003C/h2\u003E\n\u003Cp\u003EA credential represents a secret or factor used for authentication.\u003C/p\u003E\n\u003Cp\u003EExamples:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EPassword\u003C/li\u003E\n\u003Cli\u003EOTP (future)\u003C/li\u003E\n\u003Cli\u003EExternal providers (future)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022password-credential\u0022\u003E\uD83D\uDD12 Password Credential\u003C/h2\u003E\n\u003Cp\u003EThe default credential type is password.\u003C/p\u003E\n\u003Cp\u003EA password credential contains:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EHashed secret (never raw password)\u003C/li\u003E\n\u003Cli\u003ESecurity state (active, revoked, expired)\u003C/li\u003E\n\u003Cli\u003EMetadata (last used, source, etc.)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Credentials are always stored securely and validated through hashing.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022credential-validation\u0022\u003E\u2699\uFE0F Credential Validation\u003C/h2\u003E\n\u003Cp\u003ECredential validation is handled by a validator:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EVerifies secret using hashing\u003C/li\u003E\n\u003Cli\u003EChecks credential usability (revoked, expired, etc.)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Validation is isolated from business logic.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022integration-with-users\u0022\u003E\uD83D\uDD17 Integration with Users\u003C/h2\u003E\n\u003Cp\u003ECredentials are NOT created directly inside user logic.\u003C/p\u003E\n\u003Cp\u003EInstead:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 They are integrated via lifecycle hooks\u003C/p\u003E\n\u003Cp\u003EExample:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EWhen a user is created \u2192 password credential may be created\u003C/li\u003E\n\u003Cli\u003EWhen a user is deleted \u2192 credentials are removed\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This keeps domains decoupled.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022credential-lifecycle\u0022\u003E\uD83D\uDD04 Credential Lifecycle\u003C/h2\u003E\n\u003Cp\u003ECredentials support:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECreation\u003C/li\u003E\n\u003Cli\u003ESecret change\u003C/li\u003E\n\u003Cli\u003ERevocation\u003C/li\u003E\n\u003Cli\u003EExpiration\u003C/li\u003E\n\u003Cli\u003EDeletion\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022security-behavior\u0022\u003E\uD83D\uDD01 Security Behavior\u003C/h2\u003E\n\u003Cp\u003ECredential changes trigger security actions:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EChanging password revokes sessions\u003C/li\u003E\n\u003Cli\u003EReset flows require verification tokens\u003C/li\u003E\n\u003Cli\u003EInvalid attempts are tracked\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Credentials are tightly coupled with security.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022reset-flow\u0022\u003E\uD83D\uDD11 Reset Flow\u003C/h2\u003E\n\u003Cp\u003EPassword reset is a multi-step process:\u003C/p\u003E\n\u003Col\u003E\n\u003Cli\u003EBegin reset (generate token or code)\u003C/li\u003E\n\u003Cli\u003EValidate token\u003C/li\u003E\n\u003Cli\u003EApply new secret\u003C/li\u003E\n\u003C/ol\u003E\n\u003Cp\u003E\uD83D\uDC49 Reset flow is protected against enumeration and abuse.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EUsers define identity.\u003C/p\u003E\n\u003Cp\u003ECredentials define authentication.\u003C/p\u003E\n\u003Ch2 id=\u0022summary\u0022\u003E\uD83C\uDFAF Summary\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003ECredentials handle authentication secrets\u003C/li\u003E\n\u003Cli\u003EPassword is default but extensible\u003C/li\u003E\n\u003Cli\u003EIntegrated via lifecycle hooks\u003C/li\u003E\n\u003Cli\u003EStrong security guarantees\u003C/li\u003E\n\u003Cli\u003EFully extensible for new credential types\u003C/li\u003E\n\u003C/ul\u003E\n" +} \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/index.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/index.json new file mode 100644 index 00000000..b122602b --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/index.json @@ -0,0 +1,5 @@ +{ + "Slug": "plugin-domains/index", + "Title": "Plugin Domains", + "Html": "\n\u003Cp\u003EAuthentication alone is not enough.\u003C/p\u003E\n\u003Cp\u003EReal-world systems also require:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUser management\u003C/li\u003E\n\u003Cli\u003ECredential handling\u003C/li\u003E\n\u003Cli\u003EAuthorization rules\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 UltimateAuth provides these as \u003Cstrong\u003Eplugin domains\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022what-is-a-plugin-domain\u0022\u003E\uD83E\uDDE0 What Is a Plugin Domain?\u003C/h2\u003E\n\u003Cp\u003EA plugin domain is a \u003Cstrong\u003Emodular business layer\u003C/strong\u003E built on top of UltimateAuth.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Core handles authentication\u003Cbr /\u003E\n\uD83D\uDC49 Plugin domains handle identity and access logic\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022architecture\u0022\u003E\uD83C\uDFD7 Architecture\u003C/h2\u003E\n\u003Cp\u003EEach plugin domain is composed of multiple layers:\u003C/p\u003E\n\u003Ch3 id=\u0022bridge-package\u0022\u003E\uD83D\uDD39 Bridge Package\u003C/h3\u003E\n\u003Cp\u003EDefines the server needed bridge interfaces:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 This package provides required minimal info for server package.\u003C/p\u003E\n\u003Ch3 id=\u0022contracts-package\u0022\u003E\uD83D\uDD39 Contracts Package\u003C/h3\u003E\n\u003Cp\u003EShared models between:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EServer\u003C/li\u003E\n\u003Cli\u003EClient\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Includes DTOs, requests, responses\u003C/p\u003E\n\u003Ch3 id=\u0022reference-implementation\u0022\u003E\uD83D\uDD39 Reference Implementation\u003C/h3\u003E\n\u003Cp\u003EProvides default behavior:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EApplication services\u003C/li\u003E\n\u003Cli\u003EStore interfaces\u003C/li\u003E\n\u003Cli\u003EDefault implementations\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Acts as a production-ready baseline\u003C/p\u003E\n\u003Ch3 id=\u0022persistence-layer\u0022\u003E\uD83D\uDD39 Persistence Layer\u003C/h3\u003E\n\u003Cp\u003EProvides storage implementations:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EInMemory\u003C/li\u003E\n\u003Cli\u003EEntity Framework Core\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Additional providers (Redis, etc.) can be added\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022extensibility-model\u0022\u003E\uD83D\uDD04 Extensibility Model\u003C/h2\u003E\n\u003Cp\u003EPlugin domains are designed to be:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EReplaceable\u003C/li\u003E\n\u003Cli\u003EExtendable\u003C/li\u003E\n\u003Cli\u003EComposable\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You can implement your own persistence\u003Cbr /\u003E\n\uD83D\uDC49 You can extend behavior\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022recommended-approach\u0022\u003E\u26A0\uFE0F Recommended Approach\u003C/h2\u003E\n\u003Cp\u003EIn most cases:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 You should NOT replace a plugin domain entirely\u003C/p\u003E\n\u003Cp\u003EInstead:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUse provided implementations\u003C/li\u003E\n\u003Cli\u003EExtend via interfaces\u003C/li\u003E\n\u003Cli\u003ECustomize behavior where needed\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This ensures compatibility with the framework\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022domain-isolation\u0022\u003E\uD83D\uDD0C Domain Isolation\u003C/h2\u003E\n\u003Cp\u003EPlugin domains are designed to be \u003Cstrong\u003Efully isolated\u003C/strong\u003E from each other.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 A plugin domain does NOT reference another plugin domain\u003Cbr /\u003E\n\uD83D\uDC49 There are no direct dependencies between domains\u003C/p\u003E\n\u003Ch3 id=\u0022why\u0022\u003E\uD83E\uDDE0 Why?\u003C/h3\u003E\n\u003Cp\u003EThis ensures:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELoose coupling\u003C/li\u003E\n\u003Cli\u003EIndependent evolution\u003C/li\u003E\n\u003Cli\u003EReplaceability\u003C/li\u003E\n\u003Cli\u003EClear boundaries\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022communication-via-hooks\u0022\u003E\uD83D\uDD04 Communication via Hooks\u003C/h2\u003E\n\u003Cp\u003EPlugin domains communicate \u003Cstrong\u003Eonly through integration hooks\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EFor example:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUser domain triggers \u2192 \u003Ccode\u003EOnUserCreated\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003ECredential domain listens \u2192 creates password credential\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This is implemented via abstractions such as:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003EIUserLifecycleIntegration\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003Edomain events / integration points\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Core = authentication engine\u003Cbr /\u003E\n\uD83D\uDC49 Plugin domains = business logic\u003C/p\u003E\n\u003Ch2 id=\u0022why-this-matters\u0022\u003E\uD83C\uDFAF Why This Matters\u003C/h2\u003E\n\u003Cp\u003EThis architecture allows UltimateAuth to:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EStay modular\u003C/li\u003E\n\u003Cli\u003ESupport multiple domains\u003C/li\u003E\n\u003Cli\u003EEnable enterprise customization\u003C/li\u003E\n\u003Cli\u003EAvoid monolithic identity systems\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You don\u2019t build everything from scratch\u003Cbr /\u003E\n\uD83D\uDC49 You assemble what you need\u003C/p\u003E\n\u003Ch2 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EManage users \u2192 Users\u003C/li\u003E\n\u003Cli\u003EHandle credentials \u2192 Credentials\u003C/li\u003E\n\u003Cli\u003EControl access \u2192 Authorization\u003C/li\u003E\n\u003C/ul\u003E\n" +} \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/policies.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/policies.json new file mode 100644 index 00000000..a02f6401 --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/policies.json @@ -0,0 +1,5 @@ +{ + "Slug": "plugin-domains/policies", + "Title": "Policies", + "Html": "\n\u003Cp\u003EUltimateAuth uses a \u003Cstrong\u003Epolicy-driven authorization model\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EPolicies are not simple checks \u2014\u003Cbr /\u003E\nthey are \u003Cstrong\u003Ecomposable decision units\u003C/strong\u003E evaluated at runtime.\u003C/p\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EAuthorization in UltimateAuth is:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Context-based\u003Cbr /\u003E\n\uD83D\uDC49 Policy-driven\u003Cbr /\u003E\n\uD83D\uDC49 Orchestrated\u003C/p\u003E\n\u003Ch3 id=\u0022flow\u0022\u003EFlow\u003C/h3\u003E\n\u003Col\u003E\n\u003Cli\u003EBuild \u003Ccode\u003EAccessContext\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003EResolve policies\u003C/li\u003E\n\u003Cli\u003EExecute authority\u003C/li\u003E\n\u003Cli\u003EAllow / Deny / Reauth\u003C/li\u003E\n\u003C/ol\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022accesscontext\u0022\u003E\u2699\uFE0F AccessContext\u003C/h2\u003E\n\u003Cp\u003EEvery authorization decision is based on:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EActor (who is calling)\u003C/li\u003E\n\u003Cli\u003ETarget (what is being accessed)\u003C/li\u003E\n\u003Cli\u003EAction (what is being done)\u003C/li\u003E\n\u003Cli\u003ETenant\u003C/li\u003E\n\u003Cli\u003EClaims / permissions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022policy-resolution\u0022\u003E\uD83D\uDD0C Policy Resolution\u003C/h2\u003E\n\u003Cp\u003EPolicies are resolved using:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAction prefix matching\u003C/li\u003E\n\u003Cli\u003ERuntime filtering (\u003Ccode\u003EAppliesTo\u003C/code\u003E)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EExample:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003Eusers.create.admin\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003Eusers.*\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003Eauthorization.roles.*\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022policy-types\u0022\u003E\uD83E\uDDE9 Policy Types\u003C/h2\u003E\n\u003Ch3 id=\u0022global-policies\u0022\u003EGlobal Policies\u003C/h3\u003E\n\u003Cp\u003EAlways evaluated:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERequireAuthenticated\u003C/li\u003E\n\u003Cli\u003EDenyCrossTenant\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022runtime-policies\u0022\u003ERuntime Policies\u003C/h3\u003E\n\u003Cp\u003EResolved dynamically:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERequireActiveUser\u003C/li\u003E\n\u003Cli\u003EMustHavePermission\u003C/li\u003E\n\u003Cli\u003ERequireSelf\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022invariants\u0022\u003EInvariants\u003C/h3\u003E\n\u003Cp\u003EExecuted first:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECannot be bypassed\u003C/li\u003E\n\u003Cli\u003EHard security rules\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022policy-evaluation\u0022\u003E\u2696\uFE0F Policy Evaluation\u003C/h2\u003E\n\u003Cp\u003EEvaluation order:\u003C/p\u003E\n\u003Col\u003E\n\u003Cli\u003EInvariants\u003C/li\u003E\n\u003Cli\u003EGlobal policies\u003C/li\u003E\n\u003Cli\u003ERuntime policies\u003C/li\u003E\n\u003C/ol\u003E\n\u003Cp\u003E\uD83D\uDC49 First deny wins\u003Cbr /\u003E\n\uD83D\uDC49 Allow means \u201Cno objection\u201D\u003Cbr /\u003E\n\uD83D\uDC49 Reauth can be requested\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022example-policy\u0022\u003E\uD83D\uDD10 Example Policy\u003C/h2\u003E\n\u003Ch3 id=\u0022deny-admin-self-modification\u0022\u003EDeny Admin Self Modification\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EBlocks admin modifying own account\u003C/li\u003E\n\u003Cli\u003EApplies only to \u003Ccode\u003E.admin\u003C/code\u003E actions\u003C/li\u003E\n\u003Cli\u003EIgnores read operations\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022require-active-user\u0022\u003ERequire Active User\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EEnsures user exists\u003C/li\u003E\n\u003Cli\u003EEnsures user is active\u003C/li\u003E\n\u003Cli\u003ESkips anonymous actions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022access-orchestrator\u0022\u003E\uD83D\uDE80 Access Orchestrator\u003C/h2\u003E\n\u003Cp\u003EThe orchestrator is the entry point:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEnriches context (claims, permissions)\u003C/li\u003E\n\u003Cli\u003EResolves policies\u003C/li\u003E\n\u003Cli\u003EExecutes authority\u003C/li\u003E\n\u003Cli\u003ERuns command if allowed\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022key-principles\u0022\u003E\uD83C\uDFAF Key Principles\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EPolicies are composable\u003C/li\u003E\n\u003Cli\u003EAuthorization is deterministic\u003C/li\u003E\n\u003Cli\u003ENo hidden magic\u003C/li\u003E\n\u003Cli\u003EFully extensible\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Cp\u003E\uD83D\uDC49 Authorization is not a single check\u003Cbr /\u003E\n\uD83D\uDC49 It is a \u003Cstrong\u003Epipeline of decisions\u003C/strong\u003E\u003C/p\u003E\n" +} \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/users-domain.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/users-domain.json new file mode 100644 index 00000000..188e1090 --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/users-domain.json @@ -0,0 +1,5 @@ +{ + "Slug": "plugin-domains/users-domain", + "Title": "Users", + "Html": "\n\u003Cp\u003EUsers in UltimateAuth are not a single entity.\u003C/p\u003E\n\u003Cp\u003EInstead, they are composed of multiple parts that together define identity.\u003C/p\u003E\n\u003Ch2 id=\u0022core-concept\u0022\u003E\uD83E\uDDE0 Core Concept\u003C/h2\u003E\n\u003Cp\u003EA user is represented by three main components:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELifecycle (security \u0026amp; state)\u003C/li\u003E\n\u003Cli\u003EIdentifiers (login surface)\u003C/li\u003E\n\u003Cli\u003EProfile (user data)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022lifecycle-security-anchor\u0022\u003E\uD83D\uDD10 Lifecycle (Security Anchor)\u003C/h2\u003E\n\u003Cp\u003ELifecycle defines:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EWhen a user created\u003C/li\u003E\n\u003Cli\u003EWhether a user is active, suspended, or deleted\u003C/li\u003E\n\u003Cli\u003ESecurity version (used for invalidating sessions/tokens)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This is the core of authentication security.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022identifiers-login-system\u0022\u003E\uD83D\uDD11 Identifiers (Login System)\u003C/h2\u003E\n\u003Cp\u003EIdentifiers represent how a user logs in:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEmail\u003C/li\u003E\n\u003Cli\u003EUsername\u003C/li\u003E\n\u003Cli\u003EPhone\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EEach identifier has:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ENormalized value\u003C/li\u003E\n\u003Cli\u003EPrimary flag\u003C/li\u003E\n\u003Cli\u003EVerification state\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022login-identifiers\u0022\u003E\u2B50 Login Identifiers\u003C/h3\u003E\n\u003Cp\u003ENot all identifiers are used for login.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Only \u003Cstrong\u003Eprimary identifiers\u003C/strong\u003E are considered login identifiers.\u003C/p\u003E\n\u003Ch4 id=\u0022configurable-behavior\u0022\u003E\u2699\uFE0F Configurable Behavior\u003C/h4\u003E\n\u003Cp\u003EDevelopers can control:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EWhich identifier types are allowed for login\u003C/li\u003E\n\u003Cli\u003EWhether email/phone must be verified\u003C/li\u003E\n\u003Cli\u003EWhether multiple identifiers are allowed\u003C/li\u003E\n\u003Cli\u003EGlobal uniqueness rules\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch4 id=\u0022custom-login-logic\u0022\u003E\uD83D\uDD0C Custom Login Logic\u003C/h4\u003E\n\u003Cp\u003EUltimateAuth allows custom login identifier resolution.\u003C/p\u003E\n\u003Cp\u003EYou can:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAdd custom identifier types\u003C/li\u003E\n\u003Cli\u003EOverride resolution logic\u003C/li\u003E\n\u003Cli\u003EImplement your own resolver\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This means login is fully extensible.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022profile-user-data\u0022\u003E\uD83E\uDDFE Profile (User Data)\u003C/h2\u003E\n\u003Cp\u003EProfile contains non-auth data:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EName\u003C/li\u003E\n\u003Cli\u003EBio\u003C/li\u003E\n\u003Cli\u003ELocalization\u003C/li\u003E\n\u003Cli\u003EMetadata\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This is not used for authentication.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022application-service\u0022\u003E\u2699\uFE0F Application Service\u003C/h2\u003E\n\u003Cp\u003EUser operations are handled by an orchestration layer.\u003C/p\u003E\n\u003Cp\u003EIt is responsible for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECreating users\u003C/li\u003E\n\u003Cli\u003EManaging identifiers\u003C/li\u003E\n\u003Cli\u003EApplying validation rules\u003C/li\u003E\n\u003Cli\u003ETriggering integrations\u003C/li\u003E\n\u003Cli\u003ERevoking sessions when needed\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83D\uDD01 Mental Model\u003C/h2\u003E\n\u003Cp\u003EAuthentication answers:\u003C/p\u003E\n\u003Cp\u003E\u2192 Who are you?\u003C/p\u003E\n\u003Cp\u003EUsers domain answers:\u003C/p\u003E\n\u003Cp\u003E\u2192 What is this user?\u003C/p\u003E\n\u003Ch2 id=\u0022summary\u0022\u003E\uD83C\uDFAF Summary\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EUsers are composed, not singular\u003C/li\u003E\n\u003Cli\u003ELogin is based on identifiers\u003C/li\u003E\n\u003Cli\u003ELogin identifiers are configurable\u003C/li\u003E\n\u003Cli\u003ESystem is extensible by design\u003C/li\u003E\n\u003C/ul\u003E\n" +} \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/readme.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/readme.json new file mode 100644 index 00000000..cdbd470c --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/readme.json @@ -0,0 +1,5 @@ +{ + "Slug": "readme", + "Title": "\uD83D\uDD10 UltimateAuth Docs", + "Html": "\n\u003Cp\u003EThe modern, unified auth framework with platform-level abilities for .NET - Reimagined.\u003C/p\u003E\n\u003Ch2 id=\u0022sections\u0022\u003ESections\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth docs consists of 7 sections. There are suggested read line for starters:\u003C/p\u003E\n\u003Ch3 id=\u0022getting-started\u0022\u003E1) Getting Started\u003C/h3\u003E\n\u003Cp\u003EStart here to integrate UltimateAuth into your application:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EGetting Started Guide\u003C/li\u003E\n\u003Cli\u003ESetup with different client profiles\u003C/li\u003E\n\u003Cli\u003EBasic usage\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022fundamentals\u0022\u003E2) Fundamentals\u003C/h3\u003E\n\u003Cp\u003EUnderstand how UltimateAuth works internally:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECore Concepts\u003C/li\u003E\n\u003Cli\u003ESession Architecture (Root \u2192 Chain \u2192 Session)\u003C/li\u003E\n\u003Cli\u003EToken Model\u003C/li\u003E\n\u003Cli\u003EAuthorization Model\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022auth-flows\u0022\u003E3) Auth Flows\u003C/h3\u003E\n\u003Cp\u003EExplore authentication lifecycle:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogin Flow\u003C/li\u003E\n\u003Cli\u003ERefresh Flow\u003C/li\u003E\n\u003Cli\u003ELogout Flow\u003C/li\u003E\n\u003Cli\u003ESession Lifecycle\u003C/li\u003E\n\u003Cli\u003EToken Behavior\u003C/li\u003E\n\u003Cli\u003EDevice Management\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022plugin-domains\u0022\u003E4) Plugin Domains\u003C/h3\u003E\n\u003Cp\u003EUltimateAuth is built as modular domains:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUsers\u003C/li\u003E\n\u003Cli\u003ECredentials\u003C/li\u003E\n\u003Cli\u003EAuthorization\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022configuration-extensibility\u0022\u003E5) Configuration \u0026amp; Extensibility\u003C/h3\u003E\n\u003Cp\u003ECustomize behavior and integrate with your system:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EServer Options\u003C/li\u003E\n\u003Cli\u003EClient Options\u003C/li\u003E\n\u003Cli\u003EConfiguration Sources\u003C/li\u003E\n\u003Cli\u003EAdvanced Configuration\u003C/li\u003E\n\u003Cli\u003EExtending Domains \u0026amp; Stores\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022client-api\u0022\u003E6) Client \u0026amp; API\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EReal world usage of UltimateAuth\u003C/li\u003E\n\u003Cli\u003EAPI and code examples of authentication, sessions, users, credentials and authorization\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022security-advanced\u0022\u003E7) Security \u0026amp; Advanced\u003C/h3\u003E\n\u003Cp\u003EDeep dive into the security model:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession Security Model\u003C/li\u003E\n\u003Cli\u003ERefresh Token Rotation\u003C/li\u003E\n\u003Cli\u003EAccess Token Behavior\u003C/li\u003E\n\u003Cli\u003EPolicy Pipeline\u003C/li\u003E\n\u003C/ul\u003E\n" +} \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/access-token-behavior.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/access-token-behavior.json new file mode 100644 index 00000000..50f2f81e --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/access-token-behavior.json @@ -0,0 +1,5 @@ +{ + "Slug": "security/access-token-behavior", + "Title": "Access Token Behavior", + "Html": "\n\u003Cp\u003EAccess tokens in UltimateAuth are intentionally \u003Cstrong\u003Eshort-lived and mode-aware\u003C/strong\u003E.\nThey are not the primary source of truth for authentication.\u003C/p\u003E\n\u003Ch2 id=\u0022core-principle\u0022\u003E\uD83E\uDDE0 Core Principle\u003C/h2\u003E\n\u003Cp\u003EIn UltimateAuth:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Access tokens are \u003Cstrong\u003Etransport tokens\u003C/strong\u003E\n\uD83D\uDC49 Sessions are the \u003Cstrong\u003Esource of truth\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022token-types\u0022\u003E\uD83D\uDD11 Token Types\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth supports two access token types:\u003C/p\u003E\n\u003Ch3 id=\u0022opaque-tokens\u0022\u003E\uD83D\uDD39 Opaque Tokens\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ERandom, non-readable values\u003C/li\u003E\n\u003Cli\u003EStored and validated server-side\u003C/li\u003E\n\u003Cli\u003ETypically used with session-based auth\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022jwt-tokens\u0022\u003E\uD83D\uDD39 JWT Tokens\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ESelf-contained tokens\u003C/li\u003E\n\u003Cli\u003EContain claims (user, tenant, session, etc.)\u003C/li\u003E\n\u003Cli\u003ESigned and verifiable without storage\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022mode-dependent-behavior\u0022\u003E\u2699\uFE0F Mode-Dependent Behavior\u003C/h2\u003E\n\u003Cp\u003EAccess token behavior depends on auth mode:\u003C/p\u003E\n\u003Ch3 id=\u0022pureopaque\u0022\u003E\uD83D\uDFE2 PureOpaque\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ENo JWT issued\u003C/li\u003E\n\u003Cli\u003ESession cookie is the primary mechanism\u003C/li\u003E\n\u003Cli\u003EAccess token may not exist externally\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Validation = session validation\u003C/p\u003E\n\u003Ch3 id=\u0022hybrid\u0022\u003E\uD83D\uDFE1 Hybrid\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EOpaque \u002B JWT together\u003C/li\u003E\n\u003Cli\u003ESession still authoritative\u003C/li\u003E\n\u003Cli\u003EJWT used for API access\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Validation = session \u002B token\u003C/p\u003E\n\u003Ch3 id=\u0022semihybrid\u0022\u003E\uD83D\uDFE0 SemiHybrid\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EJWT is primary access token\u003C/li\u003E\n\u003Cli\u003ESession still exists\u003C/li\u003E\n\u003Cli\u003ERefresh rotation enabled\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Balanced approach\u003C/p\u003E\n\u003Ch3 id=\u0022purejwt\u0022\u003E\uD83D\uDD35 PureJwt\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EOnly JWT \u002B refresh tokens\u003C/li\u003E\n\u003Cli\u003ENo session state required\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Stateless mode\u003C/p\u003E\n\u003Ch2 id=\u0022lifetime-strategy\u0022\u003E\u23F1 Lifetime Strategy\u003C/h2\u003E\n\u003Cp\u003EAccess tokens are:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eshort-lived\u003C/li\u003E\n\u003Cli\u003Ereplaceable\u003C/li\u003E\n\u003Cli\u003Enot trusted long-term\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003ETypical lifetime:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003E5\u201315 minutes\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022refresh-interaction\u0022\u003E\uD83D\uDD04 Refresh Interaction\u003C/h2\u003E\n\u003Cp\u003EAccess tokens are never extended directly.\u003C/p\u003E\n\u003Cp\u003EInstead:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EAccess Token \u2192 expires \u2192 Refresh \u2192 new Access Token\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This ensures:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eforward-only security\u003C/li\u003E\n\u003Cli\u003Eno silent extension\u003C/li\u003E\n\u003Cli\u003Ereplay window minimization\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022claims-model\u0022\u003E\uD83E\uDDFE Claims Model\u003C/h2\u003E\n\u003Cp\u003EJWT tokens may include:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Esub (user id)\u003C/li\u003E\n\u003Cli\u003Etenant\u003C/li\u003E\n\u003Cli\u003Esid (session id)\u003C/li\u003E\n\u003Cli\u003Ejti (token id)\u003C/li\u003E\n\u003Cli\u003Ecustom claims\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Claims are generated at issuance time\n\uD83D\uDC49 Not dynamically updated afterward\u003C/p\u003E\n\u003Ch2 id=\u0022important-implication\u0022\u003E\u26A0\uFE0F Important Implication\u003C/h2\u003E\n\u003Cp\u003EIf something changes:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Euser roles\u003C/li\u003E\n\u003Cli\u003Epermissions\u003C/li\u003E\n\u003Cli\u003Esecurity state\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 existing JWTs are NOT updated\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 This is why:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Etokens are short-lived\u003C/li\u003E\n\u003Cli\u003Erefresh is required\u003C/li\u003E\n\u003Cli\u003Esession validation may still apply\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022security-boundaries\u0022\u003E\uD83D\uDEE1 Security Boundaries\u003C/h2\u003E\n\u003Cp\u003EAccess tokens are:\u003C/p\u003E\n\u003Cp\u003E\u274C not revocable individually (JWT)\n\u274C not long-term identity\u003C/p\u003E\n\u003Cp\u003E\u2714 tied to session or refresh flow\n\u2714 bounded by expiration\u003C/p\u003E\n\u003Ch2 id=\u0022why-this-matters\u0022\u003E\uD83D\uDD25 Why This Matters\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth avoids a common mistake:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 treating JWT as the system of record\u003C/p\u003E\n\u003Cp\u003EInstead:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 JWT is a \u003Cstrong\u003Esnapshot\u003C/strong\u003E, not truth\u003C/p\u003E\n\u003Ch2 id=\u0022design-tradeoff\u0022\u003E\u26A0\uFE0F Design Tradeoff\u003C/h2\u003E\n\u003Cp\u003EShort-lived tokens mean:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Emore refresh calls\u003C/li\u003E\n\u003Cli\u003Eslightly more backend interaction\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 But this enables:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Esafer rotation\u003C/li\u003E\n\u003Cli\u003Ebetter revocation\u003C/li\u003E\n\u003Cli\u003Ereduced attack window\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Access tokens are \u003Cstrong\u003Etemporary access grants\u003C/strong\u003E\n\uD83D\uDC49 Not persistent identity\u003C/p\u003E\n\u003Ch2 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003EPolicy Pipeline Deep Dive\u003C/strong\u003E to understand how access decisions are enforced.\u003C/p\u003E\n" +} \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/policy-pipeline.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/policy-pipeline.json new file mode 100644 index 00000000..48e2d245 --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/policy-pipeline.json @@ -0,0 +1,5 @@ +{ + "Slug": "security/policy-pipeline", + "Title": "Policy Pipeline", + "Html": "\n\u003Cp\u003EUltimateAuth does not rely on simple role checks.\u003C/p\u003E\n\u003Cp\u003EAuthorization is executed through a \u003Cstrong\u003Emulti-stage policy pipeline\u003C/strong\u003E that evaluates:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Einvariants (always enforced rules)\u003C/li\u003E\n\u003Cli\u003Eglobal policies\u003C/li\u003E\n\u003Cli\u003Eruntime (action-based) policies\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022core-principle\u0022\u003E\uD83E\uDDE0 Core Principle\u003C/h2\u003E\n\u003Cp\u003E\uD83D\uDC49 Authorization is not a single check\u003Cbr /\u003E\n\uD83D\uDC49 It is a \u003Cstrong\u003Edecision pipeline\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022high-level-flow\u0022\u003E\uD83D\uDD01 High-Level Flow\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EAccessContext\n \u2193\nEnrichment (claims, permissions)\n \u2193\nInvariants\n \u2193\nGlobal Policies\n \u2193\nRuntime Policies (action-based)\n \u2193\nFinal Decision\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022accesscontext\u0022\u003E\uD83E\uDDE9 AccessContext\u003C/h2\u003E\n\u003Cp\u003EEvery authorization decision is based on an \u003Ccode\u003EAccessContext\u003C/code\u003E.\u003C/p\u003E\n\u003Cp\u003EIt contains:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eactor (who is making the request)\u003C/li\u003E\n\u003Cli\u003Etarget (resource / user)\u003C/li\u003E\n\u003Cli\u003Etenant\u003C/li\u003E\n\u003Cli\u003Eaction (string-based, e.g. \u0026quot;users.delete\u0026quot;)\u003C/li\u003E\n\u003Cli\u003Eattributes (claims, permissions, etc.)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Everything in the pipeline reads from this context\u003C/p\u003E\n\u003Ch2 id=\u0022step-1-context-enrichment\u0022\u003E\uD83D\uDD04 Step 1: Context Enrichment\u003C/h2\u003E\n\u003Cp\u003EBefore policies run, context is enriched:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Epermissions are loaded\u003C/li\u003E\n\u003Cli\u003Ecompiled into \u003Ccode\u003ECompiledPermissionSet\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003Eattached to context\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This allows policies to run without hitting storage repeatedly\u003C/p\u003E\n\u003Ch2 id=\u0022step-2-invariants\u0022\u003E\uD83D\uDEE1 Step 2: Invariants\u003C/h2\u003E\n\u003Cp\u003EInvariants are \u003Cstrong\u003Ealways enforced rules\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EThey run first and cannot be bypassed.\u003C/p\u003E\n\u003Cp\u003EExamples:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Euser must be authenticated\u003C/li\u003E\n\u003Cli\u003Ecross-tenant access is denied\u003C/li\u003E\n\u003Cli\u003Erequest must be valid\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 If an invariant fails:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003E\u2192 DENY immediately\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022step-3-global-policies\u0022\u003E\uD83C\uDF10 Step 3: Global Policies\u003C/h2\u003E\n\u003Cp\u003EGlobal policies apply to all requests but are conditional.\u003C/p\u003E\n\u003Cp\u003EExamples:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERequireActiveUserPolicy\u003C/li\u003E\n\u003Cli\u003EDenyAdminSelfModificationPolicy\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EEach policy:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Edecides if it applies (\u003Ccode\u003EAppliesTo\u003C/code\u003E)\u003C/li\u003E\n\u003Cli\u003Eevaluates (\u003Ccode\u003EDecide\u003C/code\u003E)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Important:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u201CAllow\u201D means \u003Cem\u003Eno objection\u003C/em\u003E\u003C/li\u003E\n\u003Cli\u003Enot final approval\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022step-4-runtime-policies\u0022\u003E\uD83C\uDFAF Step 4: Runtime Policies\u003C/h2\u003E\n\u003Cp\u003ERuntime policies are:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eaction-based\u003C/li\u003E\n\u003Cli\u003Edynamically resolved\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThey come from:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EAccessPolicyRegistry \u2192 CompiledAccessPolicySet\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EPolicies are selected by:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003Econtext.Action.StartsWith(prefix)\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EExample:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EAction: users.delete.admin\n\nMatches:\n- users.*\n- users.delete.*\n- users.delete.admin\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022step-5-decision-engine\u0022\u003E\u2696\uFE0F Step 5: Decision Engine\u003C/h2\u003E\n\u003Cp\u003EAll policies are evaluated by:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ccode\u003EIAccessAuthority\u003C/code\u003E\u003C/p\u003E\n\u003Cp\u003EEvaluation order:\u003C/p\u003E\n\u003Col\u003E\n\u003Cli\u003EInvariants\u003C/li\u003E\n\u003Cli\u003EGlobal policies\u003C/li\u003E\n\u003Cli\u003ERuntime policies\u003C/li\u003E\n\u003C/ol\u003E\n\u003Ch3 id=\u0022decision-rules\u0022\u003EDecision Rules\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EFirst \u003Cstrong\u003Edeny\u003C/strong\u003E \u2192 stops execution\u003C/li\u003E\n\u003Cli\u003E\u201CAllow\u201D \u2192 continue\u003C/li\u003E\n\u003Cli\u003E\u201CRequiresReauthentication\u201D \u2192 tracked\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EFinal result:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EAllow\nDeny(reason)\nReauthenticationRequired\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022permission-integration\u0022\u003E\uD83D\uDD10 Permission Integration\u003C/h2\u003E\n\u003Cp\u003EPermissions are not directly checked in services.\u003C/p\u003E\n\u003Cp\u003EInstead:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Epermissions are compiled\u003C/li\u003E\n\u003Cli\u003Epolicies use them\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EExample policy:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EMustHavePermissionPolicy\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EChecks:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ECompiledPermissionSet.IsAllowed(action)\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This decouples:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Epermission storage\u003C/li\u003E\n\u003Cli\u003Eauthorization logic\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022why-this-matters\u0022\u003E\uD83D\uDD25 Why This Matters\u003C/h2\u003E\n\u003Cp\u003EThis pipeline enables:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Ecomposable security rules\u003C/li\u003E\n\u003Cli\u003Econsistent enforcement\u003C/li\u003E\n\u003Cli\u003Eseparation of concerns\u003C/li\u003E\n\u003Cli\u003Eextensibility\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003ECompared to typical systems:\u003C/p\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EFeature\u003C/th\u003E\n\u003Cth\u003ETraditional\u003C/th\u003E\n\u003Cth\u003EUltimateAuth\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EInline checks\u003C/td\u003E\n\u003Ctd\u003E\u2705\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ECentral pipeline\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003E\u2705\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EPolicy composition\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003E\u2705\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EAction-based rules\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003E\u2705\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Ch2 id=\u0022design-tradeoff\u0022\u003E\u26A0\uFE0F Design Tradeoff\u003C/h2\u003E\n\u003Cp\u003EThis model introduces:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Emore abstraction\u003C/li\u003E\n\u003Cli\u003Emore components\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 But gives:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Epredictability\u003C/li\u003E\n\u003Cli\u003Eauditability\u003C/li\u003E\n\u003Cli\u003Eflexibility\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Authorization is not \u201Cif (role == admin)\u201D\u003Cbr /\u003E\n\uD83D\uDC49 It is a \u003Cstrong\u003Epipeline of decisions\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022summary\u0022\u003E\u27A1\uFE0F Summary\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth authorization:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eis policy-driven\u003C/li\u003E\n\u003Cli\u003Eruns through a structured pipeline\u003C/li\u003E\n\u003Cli\u003Eseparates invariants, global rules, and action rules\u003C/li\u003E\n\u003Cli\u003Eproduces deterministic decisions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This makes it suitable for complex, multi-tenant, security-sensitive systems\u003C/p\u003E\n" +} \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/refresh-rotation.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/refresh-rotation.json new file mode 100644 index 00000000..cc33583a --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/refresh-rotation.json @@ -0,0 +1,5 @@ +{ + "Slug": "security/refresh-rotation", + "Title": "Refresh Rotation", + "Html": "\n\u003Cp\u003ERefresh tokens in UltimateAuth are not simple long-lived tokens.\u003C/p\u003E\n\u003Cp\u003EThey are part of a \u003Cstrong\u003Erotation-based security system\u003C/strong\u003E designed to:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eprevent token replay\u003C/li\u003E\n\u003Cli\u003Edetect token theft\u003C/li\u003E\n\u003Cli\u003Eenforce forward-only session progression\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022why-rotation\u0022\u003E\uD83E\uDDE0 Why Rotation?\u003C/h2\u003E\n\u003Cp\u003EIn traditional systems:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Erefresh tokens are long-lived\u003C/li\u003E\n\u003Cli\u003Ethey can be reused multiple times\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 If stolen, an attacker can:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Esilently keep refreshing access tokens\u003C/li\u003E\n\u003Cli\u003Estay undetected\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EUltimateAuth solves this with:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Cstrong\u003Esingle-use refresh tokens (rotation)\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022rotation-model\u0022\u003E\uD83D\uDD01 Rotation Model\u003C/h2\u003E\n\u003Cp\u003EEach refresh token is:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eused exactly once\u003C/li\u003E\n\u003Cli\u003Ereplaced with a new token\u003C/li\u003E\n\u003Cli\u003Elinked to a chain\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EToken A \u2192 Token B \u2192 Token C \u2192 ...\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EWhen a refresh happens:\u003C/p\u003E\n\u003Col\u003E\n\u003Cli\u003EToken A is validated\u003C/li\u003E\n\u003Cli\u003EToken A is \u003Cstrong\u003Erevoked\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EToken B is issued\u003C/li\u003E\n\u003Cli\u003EToken A is marked as replaced by B\u003C/li\u003E\n\u003C/ol\u003E\n\u003Ch2 id=\u0022token-state\u0022\u003E\uD83D\uDD10 Token State\u003C/h2\u003E\n\u003Cp\u003EA refresh token can be:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EActive \u2192 valid and usable\u003C/li\u003E\n\u003Cli\u003ERevoked \u2192 already used or manually revoked\u003C/li\u003E\n\u003Cli\u003EExpired \u2192 lifetime exceeded\u003C/li\u003E\n\u003Cli\u003EReplaced \u2192 already rotated\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Only \u003Cstrong\u003Eactive tokens\u003C/strong\u003E are valid\u003C/p\u003E\n\u003Ch2 id=\u0022reuse-detection\u0022\u003E\uD83D\uDEA8 Reuse Detection\u003C/h2\u003E\n\u003Cp\u003EThis is the most critical security feature.\u003C/p\u003E\n\u003Cp\u003EIf a refresh token is used \u003Cstrong\u003Eafter it has already been rotated\u003C/strong\u003E, it means:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 The token was reused\u003Cbr /\u003E\n\uD83D\uDC49 Likely stolen\u003C/p\u003E\n\u003Ch3 id=\u0022what-happens\u0022\u003EWhat happens?\u003C/h3\u003E\n\u003Cp\u003EWhen reuse is detected:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Ethe system identifies the session chain\u003C/li\u003E\n\u003Cli\u003Ethe entire chain can be revoked\u003C/li\u003E\n\u003Cli\u003Eall related sessions become invalid\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This immediately cuts off both:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eattacker\u003C/li\u003E\n\u003Cli\u003Elegitimate user (forcing reauthentication)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022chain-awareness\u0022\u003E\uD83D\uDD17 Chain Awareness\u003C/h2\u003E\n\u003Cp\u003ERefresh tokens belong to a \u003Cstrong\u003Esession chain\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EThis enables:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Etracking rotation history\u003C/li\u003E\n\u003Cli\u003Edetecting anomalies\u003C/li\u003E\n\u003Cli\u003Eapplying revocation at the correct scope\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EWithout chains:\u003C/p\u003E\n\u003Cp\u003E\u274C You cannot safely detect reuse\u003Cbr /\u003E\n\u274C You cannot know which tokens belong together\u003C/p\u003E\n\u003Ch2 id=\u0022rotation-flow\u0022\u003E\uD83D\uDD04 Rotation Flow\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EClient \u2192 Refresh(Token A)\n \u2192 Validate A\n \u2192 Revoke A\n \u2192 Issue B\n \u2192 Return new tokens\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022invalid-scenarios\u0022\u003E\u26A0\uFE0F Invalid Scenarios\u003C/h2\u003E\n\u003Ch3 id=\u0022expired-token\u0022\u003E1. Expired Token\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EToken expired \u2192 reject\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022revoked-token\u0022\u003E2. Revoked Token\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EToken already used \u2192 reuse detected\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022session-mismatch\u0022\u003E3. Session Mismatch\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EToken does not belong to expected session \u2192 reject\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022security-guarantees\u0022\u003E\uD83E\uDDE0 Security Guarantees\u003C/h2\u003E\n\u003Cp\u003ERotation ensures:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Erefresh tokens are forward-only\u003C/li\u003E\n\u003Cli\u003Eold tokens cannot be reused safely\u003C/li\u003E\n\u003Cli\u003Estolen tokens are detectable\u003C/li\u003E\n\u003Cli\u003Ecompromise triggers containment\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022why-this-matters\u0022\u003E\uD83D\uDD25 Why This Matters\u003C/h2\u003E\n\u003Cp\u003ECompared to traditional refresh tokens:\u003C/p\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EFeature\u003C/th\u003E\n\u003Cth\u003ETraditional\u003C/th\u003E\n\u003Cth\u003EUltimateAuth\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EReusable tokens\u003C/td\u003E\n\u003Ctd\u003E\u2705\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EReuse detection\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003E\u2705\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EChain awareness\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003E\u2705\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EAutomatic containment\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003E\u2705\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Ch2 id=\u0022design-tradeoff\u0022\u003E\u26A0\uFE0F Design Tradeoff\u003C/h2\u003E\n\u003Cp\u003ERotation requires:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Etoken storage\u003C/li\u003E\n\u003Cli\u003Estate tracking\u003C/li\u003E\n\u003Cli\u003Eadditional validation logic\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 UltimateAuth chooses security over simplicity.\u003C/p\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 A refresh token is not a reusable key\u003Cbr /\u003E\n\uD83D\uDC49 It is a \u003Cstrong\u003Eone-time step in a chain\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003EAccess Token Behavior\u003C/strong\u003E to understand how short-lived tokens interact with rotation.\u003C/p\u003E\n" +} \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/session-security-model.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/session-security-model.json new file mode 100644 index 00000000..9a5415f3 --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/session-security-model.json @@ -0,0 +1,5 @@ +{ + "Slug": "security/session-security-model", + "Title": "Session Security Model", + "Html": "\n\u003Cp\u003EUltimateAuth is built around a \u003Cstrong\u003Ehierarchical session model\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EThis is the foundation of its security design.\u003C/p\u003E\n\u003Ch2 id=\u0022why-a-session-model\u0022\u003E\uD83E\uDDE0 Why a Session Model?\u003C/h2\u003E\n\u003Cp\u003EMany systems treat authentication as one of these:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Ea cookie\u003C/li\u003E\n\u003Cli\u003Ea bearer token\u003C/li\u003E\n\u003Cli\u003Ea login flag\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThat works for simple apps.\u003C/p\u003E\n\u003Cp\u003EIt breaks down when you need:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eper-device isolation\u003C/li\u003E\n\u003Cli\u003Etargeted revocation\u003C/li\u003E\n\u003Cli\u003Esecurity state propagation\u003C/li\u003E\n\u003Cli\u003Ereliable reauthentication boundaries\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EUltimateAuth solves this by modeling authentication as:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ERoot \u2192 Chain \u2192 Session\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022the-three-layers\u0022\u003E\uD83E\uDDE9 The Three Layers\u003C/h2\u003E\n\u003Ch3 id=\u0022root\u0022\u003E\uD83D\uDD39 Root\u003C/h3\u003E\n\u003Cp\u003EA \u003Cstrong\u003ERoot\u003C/strong\u003E represents the user-level authentication authority.\u003C/p\u003E\n\u003Cp\u003EIt is:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Etenant-bound\u003C/li\u003E\n\u003Cli\u003Euser-bound\u003C/li\u003E\n\u003Cli\u003Elong-lived\u003C/li\u003E\n\u003Cli\u003Esecurity-versioned\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThe root is the highest-level trust anchor for authentication state.\u003C/p\u003E\n\u003Ch3 id=\u0022chain\u0022\u003E\uD83D\uDCF1 Chain\u003C/h3\u003E\n\u003Cp\u003EA \u003Cstrong\u003EChain\u003C/strong\u003E represents a device-level authentication boundary.\u003C/p\u003E\n\u003Cp\u003EIt groups sessions that belong to the same device or client context.\u003C/p\u003E\n\u003Cp\u003EA chain is where UltimateAuth models:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Edevice continuity\u003C/li\u003E\n\u003Cli\u003Etouch activity\u003C/li\u003E\n\u003Cli\u003Erotation tracking\u003C/li\u003E\n\u003Cli\u003Edevice-level revoke\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022session\u0022\u003E\uD83D\uDD11 Session\u003C/h3\u003E\n\u003Cp\u003EA \u003Cstrong\u003ESession\u003C/strong\u003E is a single authentication instance.\u003C/p\u003E\n\u003Cp\u003EIt is the most granular identity proof in the system.\u003C/p\u003E\n\u003Cp\u003EA session contains:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Ecreation time\u003C/li\u003E\n\u003Cli\u003Eexpiration time\u003C/li\u003E\n\u003Cli\u003Erevocation state\u003C/li\u003E\n\u003Cli\u003Esecurity version snapshot\u003C/li\u003E\n\u003Cli\u003Echain relationship\u003C/li\u003E\n\u003Cli\u003Edevice snapshot\u003C/li\u003E\n\u003Cli\u003Eclaims snapshot\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022relationship-model\u0022\u003E\uD83D\uDD17 Relationship Model\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EUser\n \u2514\u2500\u2500 Root\n \u2514\u2500\u2500 Chain (device)\n \u2514\u2500\u2500 Session (login instance)\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Root answers: \u003Cstrong\u003Ewhat is the current security authority for this user?\u003C/strong\u003E\u003Cbr /\u003E\n\uD83D\uDC49 Chain answers: \u003Cstrong\u003Ewhich device context is this?\u003C/strong\u003E\u003Cbr /\u003E\n\uD83D\uDC49 Session answers: \u003Cstrong\u003Ewhich authentication instance is being used?\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022security-versioning\u0022\u003E\uD83D\uDEE1 Security Versioning\u003C/h2\u003E\n\u003Cp\u003EOne of the most important protections in UltimateAuth is \u003Cstrong\u003Esecurity versioning\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EA root maintains a security version.\u003C/p\u003E\n\u003Cp\u003EEach session stores the security version that existed at creation time.\u003C/p\u003E\n\u003Cp\u003EValidation compares them.\u003C/p\u003E\n\u003Cp\u003EIf they no longer match:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ESession \u2192 invalid\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThis is how UltimateAuth can invalidate existing sessions after events such as:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Epassword change\u003C/li\u003E\n\u003Cli\u003Ecredential reset\u003C/li\u003E\n\u003Cli\u003Eaccount recovery\u003C/li\u003E\n\u003Cli\u003Eadministrative security action\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022validation-model\u0022\u003E\uD83D\uDD0D Validation Model\u003C/h2\u003E\n\u003Cp\u003ESession validation is not a single check.\u003C/p\u003E\n\u003Cp\u003EIt is a layered verification process.\u003C/p\u003E\n\u003Cp\u003EA session is considered valid only if all of these still hold:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Ethe session exists\u003C/li\u003E\n\u003Cli\u003Ethe session is active\u003C/li\u003E\n\u003Cli\u003Ethe chain exists\u003C/li\u003E\n\u003Cli\u003Ethe chain is active\u003C/li\u003E\n\u003Cli\u003Ethe chain belongs to the expected tenant\u003C/li\u003E\n\u003Cli\u003Ethe chain matches the session\u003C/li\u003E\n\u003Cli\u003Ethe root exists\u003C/li\u003E\n\u003Cli\u003Ethe root is not revoked\u003C/li\u003E\n\u003Cli\u003Ethe root matches the chain\u003C/li\u003E\n\u003Cli\u003Ethe root security version matches the session snapshot\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022device-awareness\u0022\u003E\uD83D\uDCF1 Device Awareness\u003C/h2\u003E\n\u003Cp\u003EChains provide device-level isolation.\u003C/p\u003E\n\u003Cp\u003EWhen a device identifier is available, validation can compare:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Edevice stored on chain\u003C/li\u003E\n\u003Cli\u003Edevice presented by request\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EIf they do not match, UltimateAuth can reject the request depending on configuration.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 This makes device-bound security enforceable without turning every authentication flow into a custom implementation.\u003C/p\u003E\n\u003Ch2 id=\u0022expiration-and-activity\u0022\u003E\u23F1 Expiration and Activity\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth separates:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Esession expiration\u003C/li\u003E\n\u003Cli\u003Echain activity\u003C/li\u003E\n\u003Cli\u003Eroot security state\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThis is important.\u003C/p\u003E\n\u003Cp\u003EA session may expire because of time.\u003Cbr /\u003E\nA chain may become inactive because of idle timeout.\u003Cbr /\u003E\nA root may invalidate everything because security state changed.\u003C/p\u003E\n\u003Cp\u003EThese are different failure modes with different meanings.\u003C/p\u003E\n\u003Ch2 id=\u0022revocation-boundaries\u0022\u003E\uD83D\uDEAA Revocation Boundaries\u003C/h2\u003E\n\u003Cp\u003ERevocation is also hierarchical.\u003C/p\u003E\n\u003Ch3 id=\u0022session-revoke\u0022\u003ESession revoke\u003C/h3\u003E\n\u003Cp\u003EInvalidates one authentication instance.\u003C/p\u003E\n\u003Ch3 id=\u0022chain-revoke\u0022\u003EChain revoke\u003C/h3\u003E\n\u003Cp\u003EInvalidates all sessions for one device.\u003C/p\u003E\n\u003Ch3 id=\u0022root-revoke\u0022\u003ERoot revoke\u003C/h3\u003E\n\u003Cp\u003EInvalidates all chains and sessions for the user.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 This gives UltimateAuth targeted containment.\u003C/p\u003E\n\u003Cp\u003EInstead of \u201Clog out everywhere or nowhere,\u201D\u003Cbr /\u003E\nyou can revoke exactly the right scope.\u003C/p\u003E\n\u003Ch2 id=\u0022why-this-matters\u0022\u003E\uD83D\uDD25 Why This Matters\u003C/h2\u003E\n\u003Cp\u003EThis model gives you controls that flat token systems usually do not provide:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Erevoke one device without affecting others\u003C/li\u003E\n\u003Cli\u003Einvalidate sessions after security changes\u003C/li\u003E\n\u003Cli\u003Ereason about device trust explicitly\u003C/li\u003E\n\u003Cli\u003Eseparate authentication lifetime from token lifetime\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022design-tradeoff\u0022\u003E\u26A0\uFE0F Design Tradeoff\u003C/h2\u003E\n\u003Cp\u003EThis model is more sophisticated than plain JWT-only authentication.\u003C/p\u003E\n\u003Cp\u003EThat is intentional.\u003C/p\u003E\n\u003Cp\u003EUltimateAuth chooses:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eexplicit state\u003C/li\u003E\n\u003Cli\u003Erevocability\u003C/li\u003E\n\u003Cli\u003Etraceable security decisions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003Eover:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eminimal infrastructure\u003C/li\u003E\n\u003Cli\u003Epurely stateless assumptions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Authentication in UltimateAuth is not \u201Ca token\u201D\u003Cbr /\u003E\n\uD83D\uDC49 It is a \u003Cstrong\u003Esecurity hierarchy with revocation boundaries\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003ERefresh Token Rotation\u003C/strong\u003E to understand how continuation security works after login.\u003C/p\u003E\n" +} \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/Program.cs b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/Program.cs index d75216af..247e48c6 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/Program.cs +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/Program.cs @@ -11,6 +11,8 @@ builder.Services.AddMudServices(); builder.Services.AddMudExtensions(); +builder.Services.AddHttpClient(); + var app = builder.Build(); if (app.Environment.IsDevelopment()) diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/wwwroot/app.css b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/wwwroot/app.css index 9eede6fb..658dbe25 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/wwwroot/app.css +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/wwwroot/app.css @@ -197,3 +197,60 @@ h1:focus { position: relative; z-index: 9999; } + + + + +.docs-container { + padding-top: 32px; + padding-bottom: 64px; +} + +.docs-title { + font-weight: 600; +} + +.docs-content { + line-height: 1.7; + font-size: 15px; +} + + .docs-content h1, + .docs-content h2, + .docs-content h3 { + margin-top: 2rem; + font-weight: 600; + } + + .docs-content p { + margin: 1rem 0; + } + + .docs-content ul { + padding-left: 1.5rem; + } + + .docs-content pre { + background: #0d1117; + padding: 1rem; + border-radius: 10px; + overflow-x: auto; + margin: 1.5rem 0; + } + + .docs-content code { + font-family: Consolas, monospace; + font-size: 0.9rem; + } + + .docs-content p code { + background: rgba(135,131,120,0.15); + padding: 2px 6px; + border-radius: 6px; + } + +.docs-loading { + display: flex; + justify-content: center; + margin-top: 100px; +} diff --git a/src/utilities/CodeBeam.UltimateAuth.DocsBuilder/CodeBeam.UltimateAuth.DocsBuilder.csproj b/src/utilities/CodeBeam.UltimateAuth.DocsBuilder/CodeBeam.UltimateAuth.DocsBuilder.csproj new file mode 100644 index 00000000..4bdfdb9f --- /dev/null +++ b/src/utilities/CodeBeam.UltimateAuth.DocsBuilder/CodeBeam.UltimateAuth.DocsBuilder.csproj @@ -0,0 +1,15 @@ + + + + Exe + net10.0 + enable + enable + false + + + + + + + diff --git a/src/utilities/CodeBeam.UltimateAuth.DocsBuilder/Program.cs b/src/utilities/CodeBeam.UltimateAuth.DocsBuilder/Program.cs new file mode 100644 index 00000000..09c1791a --- /dev/null +++ b/src/utilities/CodeBeam.UltimateAuth.DocsBuilder/Program.cs @@ -0,0 +1,250 @@ +using System.Text.Json; +using Markdig; + +Console.WriteLine("=== DocsBuilder START ==="); + +var solutionRootDir = TryGetSolutionDir(args) ?? FindSolutionRootFallback(); + +Console.WriteLine($"SolutionRoot: {solutionRootDir}"); + +var sourceDocsDir = Path.Combine(solutionRootDir, "docs", "content"); +var groupsOrderPath = Path.Combine(sourceDocsDir, "_groups.json"); + +Dictionary groupOrders = new(StringComparer.OrdinalIgnoreCase); + +if (File.Exists(groupsOrderPath)) +{ + var groupsOrderJson = await File.ReadAllTextAsync(groupsOrderPath); + groupOrders = JsonSerializer.Deserialize>(groupsOrderJson) + ?? new Dictionary(StringComparer.OrdinalIgnoreCase); +} + +var websiteDocsOutputDir = Path.Combine( + solutionRootDir, + "docs", + "website", + "CodeBeam.UltimateAuth.Docs.Wasm", + "CodeBeam.UltimateAuth.Docs.Wasm.Client", + "wwwroot", + "docs" +); + +if (!Directory.Exists(sourceDocsDir)) +{ + Console.Error.WriteLine($"Docs source directory not found: {sourceDocsDir}"); + return; +} + +Directory.CreateDirectory(websiteDocsOutputDir); + +var pipeline = new MarkdownPipelineBuilder() + .UseAdvancedExtensions() + .Build(); + +var markdownFiles = Directory.GetFiles(sourceDocsDir, "*.md", SearchOption.AllDirectories); + +var allDocs = new List(); + +foreach (var file in markdownFiles) +{ + var relativePath = Path.GetRelativePath(sourceDocsDir, file); + var outputRelativePath = Path.ChangeExtension(relativePath, ".json"); + var outputPath = Path.Combine(websiteDocsOutputDir, outputRelativePath); + + var markdown = await File.ReadAllTextAsync(file); + + var (meta, content) = ParseFrontmatter(markdown); + + var title = meta.TryGetValue("title", out var metaTitle) && !string.IsNullOrWhiteSpace(metaTitle) + ? metaTitle + : ExtractTitle(content) ?? Path.GetFileNameWithoutExtension(file); + + var order = meta.ContainsKey("order") + ? int.Parse(meta["order"]) + : 999; + + var group = meta.ContainsKey("group") + ? meta["group"] + : ""; + + var slug = NormalizeSlug(relativePath); + + var groupOrder = groupOrders.TryGetValue(group, out var resolvedGroupOrder) + ? resolvedGroupOrder + : 999; + + allDocs.Add(new DocIndexItem + { + Title = title, + Slug = slug, + Order = order, + Group = group, + GroupOrder = groupOrder + }); + + var inputLastWrite = File.GetLastWriteTimeUtc(file); + + if (File.Exists(outputPath)) + { + var outputLastWrite = File.GetLastWriteTimeUtc(outputPath); + + if (outputLastWrite >= inputLastWrite) + { + Console.WriteLine($"⏩ Skipped: {relativePath}"); + continue; + } + } + + Console.WriteLine($"⚙ Processing: {relativePath}"); + + var html = Markdown.ToHtml(content, pipeline); + html = RemoveFirstH1(html); + + var outputFolder = Path.GetDirectoryName(outputPath); + if (!string.IsNullOrWhiteSpace(outputFolder)) + Directory.CreateDirectory(outputFolder); + + var doc = new DocOutput(slug, title, html); + + var json = JsonSerializer.Serialize(doc, new JsonSerializerOptions + { + WriteIndented = true + }); + + await File.WriteAllTextAsync(outputPath, json); +} + +var ordered = allDocs + .OrderBy(x => x.GroupOrder) + .ThenBy(x => x.Group) + .ThenBy(x => x.Order) + .ToList(); + +var indexJson = JsonSerializer.Serialize(ordered, new JsonSerializerOptions +{ + WriteIndented = true +}); + +await File.WriteAllTextAsync( + Path.Combine(websiteDocsOutputDir, "docs-index.json"), + indexJson +); + +Console.WriteLine("✅ Docs build completed."); + + +// ---------------- HELPERS ---------------- + +static string? ExtractTitle(string markdown) +{ + using var reader = new StringReader(markdown); + + while (reader.ReadLine() is { } line) + { + if (line.StartsWith("# ")) + return line[2..].Trim(); + } + + return null; +} + +static string NormalizeSlug(string relativePath) +{ + return Path.ChangeExtension(relativePath, null)! + .Replace('\\', '/'); +} + +static string? TryGetSolutionDir(string[] args) +{ + var index = Array.IndexOf(args, "--solutionDir"); + + if (index >= 0 && index < args.Length - 1) + { + var path = args[index + 1]; + + if (Directory.Exists(path)) + return Path.GetFullPath(path); + } + + return null; +} + +static string FindSolutionRootFallback() +{ + var dir = new DirectoryInfo(AppContext.BaseDirectory); + + for (int i = 0; i < 10; i++) + { + if (dir.GetDirectories("docs").Any()) + return dir.FullName; + + dir = dir.Parent!; + if (dir == null) break; + } + + throw new Exception("Solution root not found"); +} + +static (Dictionary meta, string content) ParseFrontmatter(string markdown) +{ + if (string.IsNullOrWhiteSpace(markdown)) + return (new(), string.Empty); + + markdown = markdown.TrimStart('\uFEFF'); + markdown = markdown.Replace("\r\n", "\n").Replace("\r", "\n"); + markdown = markdown.TrimStart(); + + if (!markdown.StartsWith("---")) + return (new(), markdown); + + var firstLineEnd = markdown.IndexOf('\n'); + if (firstLineEnd == -1) + return (new(), markdown); + + var firstLine = markdown.Substring(0, firstLineEnd).Trim(); + if (firstLine != "---") + return (new(), markdown); + + var end = markdown.IndexOf("\n---\n", firstLineEnd + 1); + if (end == -1) + return (new(), markdown); + + var rawMeta = markdown.Substring(firstLineEnd + 1, end - firstLineEnd - 1); + var content = markdown[(end + 5)..]; + + var dict = new Dictionary(StringComparer.OrdinalIgnoreCase); + + foreach (var line in rawMeta.Split('\n', StringSplitOptions.RemoveEmptyEntries)) + { + var parts = line.Split(':', 2, StringSplitOptions.TrimEntries); + + if (parts.Length == 2 && !string.IsNullOrWhiteSpace(parts[0])) + { + dict[parts[0]] = parts[1]; + } + } + + return (dict, content); +} + +static string RemoveFirstH1(string html) +{ + var start = html.IndexOf(""); + if (end == -1) return html; + + return html.Remove(start, end + 5 - start); +} + +internal sealed record DocOutput(string Slug, string Title, string Html); + +internal sealed class DocIndexItem +{ + public string Title { get; set; } = ""; + public string Slug { get; set; } = ""; + public int Order { get; set; } + public string Group { get; set; } = ""; + public int GroupOrder { get; set; } = 999; +} From b168dfc9326847d0f311b6b59eaa811c91afa3d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Can=20Karag=C3=B6z?= Date: Thu, 9 Apr 2026 22:57:06 +0300 Subject: [PATCH 3/4] Docs Nav & Toc Sections --- docs/content/fundamentals/index.md | 4 +- ...eBeam.UltimateAuth.Docs.Wasm.Client.csproj | 5 +- .../Layout/DocsLayout.razor | 7 +- .../Layout/MainLayout.razor | 5 +- .../Pages/DocsContent.razor | 121 +++++++++++++++++- .../Pages/DocsPageState.cs | 46 +++++++ .../Pages/DocsSidebar.razor | 19 ++- .../Pages/DocsToc.razor | 78 +++++++++++ .../Program.cs | 3 + .../docs/auth-flows/device-management.json | 94 +++++++++++++- .../wwwroot/docs/auth-flows/index.json | 74 ++++++++++- .../wwwroot/docs/auth-flows/login-flow.json | 99 +++++++++++++- .../wwwroot/docs/auth-flows/logout-flow.json | 119 ++++++++++++++++- .../wwwroot/docs/auth-flows/refresh-flow.json | 104 ++++++++++++++- .../docs/auth-flows/session-lifecycle.json | 99 +++++++++++++- .../docs/auth-flows/token-behavior.json | 114 ++++++++++++++++- .../wwwroot/docs/client/authentication.json | 69 +++++++++- .../wwwroot/docs/client/authorization.json | 69 +++++++++- .../wwwroot/docs/client/credentials.json | 89 ++++++++++++- .../wwwroot/docs/client/identifiers.json | 89 ++++++++++++- .../wwwroot/docs/client/index.json | 44 ++++++- .../docs/client/session-management.json | 84 +++++++++++- .../wwwroot/docs/client/user-management.json | 84 +++++++++++- .../configuration/advanced-configuration.json | 69 +++++++++- .../docs/configuration/client-options.json | 54 +++++++- .../configuration/configuration-overview.json | 69 +++++++++- .../configuration/configuration-sources.json | 84 +++++++++++- .../wwwroot/docs/configuration/index.json | 54 +++++++- .../docs/configuration/server-options.json | 74 ++++++++++- .../wwwroot/docs/fundamentals/auth-model.json | 89 ++++++++++++- .../wwwroot/docs/fundamentals/auth-modes.json | 84 +++++++++++- .../docs/fundamentals/client-profiles.json | 74 ++++++++++- .../docs/fundamentals/flow-based-auth.json | 94 +++++++++++++- .../wwwroot/docs/fundamentals/index.json | 3 +- .../docs/fundamentals/request-lifecycle.json | 94 +++++++++++++- .../fundamentals/runtime-architecture.json | 69 +++++++++- .../wwwroot/docs/getting-started/index.json | 64 ++++++++- .../docs/getting-started/quickstart.json | 59 ++++++++- .../getting-started/real-world-setup.json | 44 ++++++- .../plugin-domains/authorization-domain.json | 74 ++++++++++- .../plugin-domains/credential-domain.json | 54 +++++++- .../wwwroot/docs/plugin-domains/index.json | 74 ++++++++++- .../wwwroot/docs/plugin-domains/policies.json | 74 ++++++++++- .../docs/plugin-domains/users-domain.json | 44 ++++++- .../wwwroot/docs/readme.json | 44 ++++++- .../docs/security/access-token-behavior.json | 94 +++++++++++++- .../docs/security/policy-pipeline.json | 74 ++++++++++- .../docs/security/refresh-rotation.json | 84 +++++++++++- .../docs/security/session-security-model.json | 94 +++++++++++++- .../Components/App.razor | 1 + .../Program.cs | 1 + .../wwwroot/app.css | 18 +++ .../wwwroot/docs.js | 73 +++++++++++ .../CodeBeam.UltimateAuth.DocsBuilder.csproj | 1 + .../Program.cs | 101 ++++++++++++++- 55 files changed, 3446 insertions(+), 56 deletions(-) create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsPageState.cs create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsToc.razor create mode 100644 docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/wwwroot/docs.js diff --git a/docs/content/fundamentals/index.md b/docs/content/fundamentals/index.md index 1da5c3d3..e757b828 100644 --- a/docs/content/fundamentals/index.md +++ b/docs/content/fundamentals/index.md @@ -12,7 +12,7 @@ group: fundamentals -\# 🧠 Fundamentals +\# 🧠 Fundamental Overview @@ -20,6 +20,8 @@ This section explains how UltimateAuth works internally. +\## Section Order + If you are new, follow this order: diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/CodeBeam.UltimateAuth.Docs.Wasm.Client.csproj b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/CodeBeam.UltimateAuth.Docs.Wasm.Client.csproj index 04640ad2..db9bf894 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/CodeBeam.UltimateAuth.Docs.Wasm.Client.csproj +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/CodeBeam.UltimateAuth.Docs.Wasm.Client.csproj @@ -18,10 +18,7 @@ - +
diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/DocsLayout.razor b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/DocsLayout.razor index 7e6c42f1..e0cba0bf 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/DocsLayout.razor +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/DocsLayout.razor @@ -10,4 +10,9 @@ @Body - \ No newline at end of file + + + + + + diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/MainLayout.razor b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/MainLayout.razor index c590d2f0..4fe38e2d 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/MainLayout.razor +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/MainLayout.razor @@ -1,4 +1,5 @@ @using CodeBeam.UltimateAuth.Docs.Wasm.Client.Brand +@using CodeBeam.UltimateAuth.Docs.Wasm.Client.Pages @inherits LayoutComponentBase @inject NavigationManager Nav @@ -39,7 +40,9 @@ Home - Docs + + + Samples Donate diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsContent.razor b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsContent.razor index 5da05cd6..e2b4bbfb 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsContent.razor +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsContent.razor @@ -1,5 +1,8 @@ @using System.Net.Http.Json +@inject DocsPageState PageState @inject HttpClient Http +@inject NavigationManager Nav +@inject IJSRuntime JS @if (_loading || !RendererInfo.IsInteractive) { @@ -24,6 +27,44 @@ else if (_doc is not null)
@((MarkupString)_doc.Html)
+ + + + + + @if (_prev != null) + { + + + + Previous + + + + @_prev.Title + + + + } + + + + @if (_next != null) + { + + + + Next + + + + @_next.Title + + + + } + + } @@ -41,6 +82,9 @@ else if (_doc is not null) _loading = true; _notFound = false; _doc = null; + _prev = null; + _next = null; + PageState.Clear(); var slug = string.IsNullOrWhiteSpace(Slug) ? "index" @@ -51,7 +95,15 @@ else if (_doc is not null) _doc = await Http.GetFromJsonAsync($"docs/{slug}.json"); if (_doc == null) + { _notFound = true; + } + else + { + PageState.SetPage(slug, _doc.Title, _doc.Headings); + await EnsureNavLoaded(); + ComputePrevNext(); + } } catch { @@ -74,5 +126,72 @@ else if (_doc is not null) public string Slug { get; set; } = ""; public string Title { get; set; } = ""; public string Html { get; set; } = ""; + public List Headings { get; set; } = []; + } + + private List _nav = []; + private List? _navCache; + private DocNavItem? _prev; + private DocNavItem? _next; + + private class DocNavItem + { + public string Title { get; set; } = ""; + public string Slug { get; set; } = ""; } -} \ No newline at end of file + + private async Task EnsureNavLoaded() + { + if (_navCache is not null) + return; + + _navCache = await Http.GetFromJsonAsync>("docs/docs-index.json") ?? []; + } + + private void ComputePrevNext() + { + _prev = null; + _next = null; + + if (_navCache is null) + return; + + var currentPath = Nav.ToBaseRelativePath(Nav.Uri); + + var currentSlug = currentPath.StartsWith("docs/") + ? currentPath.Substring(5) + : currentPath; + + currentSlug = currentSlug.Trim('/'); + + var index = _navCache.FindIndex(x => + x.Slug.Trim('/').Equals(currentSlug, StringComparison.OrdinalIgnoreCase)); + + if (index > 0) + _prev = _navCache[index - 1]; + + if (index >= 0 && index < _navCache.Count - 1) + _next = _navCache[index + 1]; + } + + private async Task LoadNavAsync() + { + _nav = await Http.GetFromJsonAsync>("docs/docs-index.json") ?? []; + + var currentPath = Nav.ToBaseRelativePath(Nav.Uri); + + var currentSlug = currentPath.StartsWith("docs/") + ? currentPath.Substring(5) + : currentPath; + + var index = _nav.FindIndex(x => x.Slug == currentSlug); + + Console.WriteLine($"Slug: {currentSlug}, Index: {index}"); + + if (index > 0) + _prev = _nav[index - 1]; + + if (index >= 0 && index < _nav.Count - 1) + _next = _nav[index + 1]; + } +} diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsPageState.cs b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsPageState.cs new file mode 100644 index 00000000..78ba56dc --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsPageState.cs @@ -0,0 +1,46 @@ +namespace CodeBeam.UltimateAuth.Docs.Wasm.Client.Pages; + +public sealed class DocsPageState +{ + public string? CurrentSlug { get; private set; } + public string? CurrentTitle { get; private set; } + public IReadOnlyList Headings { get; private set; } = []; + + public string? ActiveHeadingId { get; private set; } + + public event Action? Changed; + + public void SetPage(string? slug, string? title, IReadOnlyList headings) + { + CurrentSlug = slug; + CurrentTitle = title; + Headings = headings; + ActiveHeadingId = headings.Count > 0 ? headings[0].Id : null; + Changed?.Invoke(); + } + + public void SetActiveHeading(string? id) + { + if (string.Equals(ActiveHeadingId, id, StringComparison.Ordinal)) + return; + + ActiveHeadingId = id; + Changed?.Invoke(); + } + + public void Clear() + { + CurrentSlug = null; + CurrentTitle = null; + Headings = []; + ActiveHeadingId = null; + Changed?.Invoke(); + } +} + +public sealed class DocsHeadingItem +{ + public string Id { get; set; } = ""; + public string Text { get; set; } = ""; + public int Level { get; set; } +} diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsSidebar.razor b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsSidebar.razor index d7bdc624..030151ba 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsSidebar.razor +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsSidebar.razor @@ -2,9 +2,13 @@ @inject NavigationManager Nav
- - Documentation - + @if (!Inline) + { + + UAuth Docs + + + } @@ -15,9 +19,10 @@ else { + Overview @foreach (var group in _groups) { - + @foreach (var item in group.Value) { @item.Title @@ -31,8 +36,12 @@ @code { private Dictionary>? _groups; private DocIndexItem? _selectedValue; + private bool _expanded = true; + + [Parameter] + public bool Inline { get; set; } - protected override async Task OnAfterRenderAsync(bool firstRender) + protected override async Task OnAfterRenderAsync(bool firstRender) { if (!firstRender) return; diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsToc.razor b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsToc.razor new file mode 100644 index 00000000..dae31396 --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsToc.razor @@ -0,0 +1,78 @@ +@inject DocsPageState PageState +@inject NavigationManager Nav +@inject IJSRuntime JS + +@if (PageState.Headings.Count == 0) +{ + return; +} + + + On this page + + + @foreach (var item in PageState.Headings) + { + + @item.Text + + } + + + +@code { + private DotNetObjectReference? _ref; + + protected override void OnInitialized() + { + PageState.Changed += OnStateChanged; + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + Console.WriteLine($"OnAfterRender - Headings: {PageState.Headings.Count}"); + + if (_ref == null && PageState.Headings.Count > 0) + { + Console.WriteLine("Starting ScrollSpy"); + + _ref = DotNetObjectReference.Create(this); + await JS.InvokeVoidAsync("uaDocsScrollSpy.start", _ref); + } + } + + private void OnStateChanged() + { + InvokeAsync(StateHasChanged); + } + + private string GetHref(string id) + { + return $"{Nav.Uri.Split('#')[0]}#{id}"; + } + + [JSInvokable] + public Task SetActiveHeading(string id) + { + PageState.SetActiveHeading(id); + return Task.CompletedTask; + } + + private async Task OnClick(string id) + { + PageState.SetActiveHeading(id); + await JS.InvokeVoidAsync("uaDocsScrollSpy.scrollTo", id); + } + + private string GetClass(DocsHeadingItem item) + => item.Id == PageState.ActiveHeadingId ? "active toc-active" : ""; + + private Task ScrollTo(string id) + => JS.InvokeVoidAsync("uaDocsScrollSpy.scrollTo", id).AsTask(); + + public void Dispose() + { + PageState.Changed -= OnStateChanged; + } +} diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Program.cs b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Program.cs index 2b668304..c84ac5a8 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Program.cs +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Program.cs @@ -1,3 +1,4 @@ +using CodeBeam.UltimateAuth.Docs.Wasm.Client.Pages; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using MudBlazor.Services; using MudExtensions.Services; @@ -7,6 +8,8 @@ builder.Services.AddMudServices(); builder.Services.AddMudExtensions(); +builder.Services.AddScoped(); + builder.Services.AddScoped(sp => new HttpClient { diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/device-management.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/device-management.json index 1c0cb6ad..966fac57 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/device-management.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/device-management.json @@ -1,5 +1,97 @@ { "Slug": "auth-flows/device-management", "Title": "Device Management", - "Html": "\n\u003Cp\u003EIn UltimateAuth, devices are not an afterthought.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 They are a \u003Cstrong\u003Efirst-class concept\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022why-device-matters\u0022\u003E\uD83E\uDDE0 Why Device Matters\u003C/h2\u003E\n\u003Cp\u003EMost authentication systems ignore devices.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EA user logs in\u003C/li\u003E\n\u003Cli\u003EA token is issued\u003C/li\u003E\n\u003Cli\u003EEverything is treated the same\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This breaks down when you need:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EMulti-device control\u003C/li\u003E\n\u003Cli\u003ESession visibility\u003C/li\u003E\n\u003Cli\u003ESecurity enforcement\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 UltimateAuth solves this with \u003Cstrong\u003Edevice-aware authentication\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022core-concept-chain-device\u0022\u003E\uD83E\uDDE9 Core Concept: Chain = Device\u003C/h2\u003E\n\u003Cp\u003EIn UltimateAuth:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 A \u003Cstrong\u003ESessionChain represents a device\u003C/strong\u003E\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003EDevice \u2192 Chain \u2192 Sessions\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EEach chain:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EIs bound to a device\u003C/li\u003E\n\u003Cli\u003EGroups sessions\u003C/li\u003E\n\u003Cli\u003ETracks activity\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 A device is not inferred \u2014 it is explicitly modeled\u003C/p\u003E\n\u003Ch2 id=\u0022what-defines-a-device\u0022\u003E\uD83D\uDD17 What Defines a Device?\u003C/h2\u003E\n\u003Cp\u003EA chain includes:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EDeviceId\u003C/li\u003E\n\u003Cli\u003EPlatform (web, mobile, etc.)\u003C/li\u003E\n\u003Cli\u003EOperating System\u003C/li\u003E\n\u003Cli\u003EBrowser\u003C/li\u003E\n\u003Cli\u003EIP (optional binding)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This forms a \u003Cstrong\u003Edevice fingerprint\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022device-lifecycle\u0022\u003E\uD83D\uDD04 Device Lifecycle\u003C/h2\u003E\n\u003Ch3 id=\u0022first-login\u0022\u003E1\uFE0F\u20E3 First Login\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ENew device detected\u003C/li\u003E\n\u003Cli\u003ENew chain is created\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022subsequent-logins\u0022\u003E2\uFE0F\u20E3 Subsequent Logins\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ESame device \u2192 reuse chain\u003C/li\u003E\n\u003Cli\u003ENew device \u2192 new chain\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Device continuity is preserved\u003C/p\u003E\n\u003Ch3 id=\u0022activity-touch\u0022\u003E3\uFE0F\u20E3 Activity (Touch)\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EChain \u003Ccode\u003ELastSeenAt\u003C/code\u003E updated\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003ETouchCount\u003C/code\u003E increases\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Tracks real usage\u003C/p\u003E\n\u003Ch3 id=\u0022token-rotation\u0022\u003E4\uFE0F\u20E3 Token Rotation\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession changes\u003C/li\u003E\n\u003Cli\u003EChain remains\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003ERotationCount\u003C/code\u003E increases\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Device identity stays stable\u003C/p\u003E\n\u003Ch3 id=\u0022logout\u0022\u003E5\uFE0F\u20E3 Logout\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession removed\u003C/li\u003E\n\u003Cli\u003EChain remains\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Device still trusted\u003C/p\u003E\n\u003Ch3 id=\u0022revoke\u0022\u003E6\uFE0F\u20E3 Revoke\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EChain invalidated\u003C/li\u003E\n\u003Cli\u003EAll sessions removed\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Device trust is reset\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022security-model\u0022\u003E\uD83D\uDD10 Security Model\u003C/h2\u003E\n\u003Ch3 id=\u0022device-binding\u0022\u003E\uD83D\uDD17 Device Binding\u003C/h3\u003E\n\u003Cp\u003ESessions and tokens are tied to:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EChain\u003C/li\u003E\n\u003Cli\u003EDevice context\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Prevents cross-device reuse\u003C/p\u003E\n\u003Ch3 id=\u0022rotation-tracking\u0022\u003E\uD83D\uDD01 Rotation Tracking\u003C/h3\u003E\n\u003Cp\u003EChains track:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERotationCount\u003C/li\u003E\n\u003Cli\u003ETouchCount\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Enables anomaly detection\u003C/p\u003E\n\u003Ch3 id=\u0022revoke-cascade\u0022\u003E\uD83D\uDEA8 Revoke Cascade\u003C/h3\u003E\n\u003Cp\u003EIf a device is compromised:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEntire chain can be revoked\u003C/li\u003E\n\u003Cli\u003EAll sessions invalidated\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Immediate containment\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022configuration\u0022\u003E\u2699\uFE0F Configuration\u003C/h2\u003E\n\u003Cp\u003EDevice behavior is configurable via session options:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EMax chains per user\u003C/li\u003E\n\u003Cli\u003EMax sessions per chain\u003C/li\u003E\n\u003Cli\u003EPlatform-based limits\u003C/li\u003E\n\u003Cli\u003EDevice mismatch behavior\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Fine-grained control for enterprise scenarios\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Device = Chain\u003Cbr /\u003E\n\uD83D\uDC49 Not just metadata\u003C/p\u003E\n\u003Ch2 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EDevices are explicitly modeled\u003C/li\u003E\n\u003Cli\u003EEach device has its own chain\u003C/li\u003E\n\u003Cli\u003ESessions belong to chains\u003C/li\u003E\n\u003Cli\u003ESecurity is enforced per device\u003C/li\u003E\n\u003Cli\u003ELogout and revoke operate on device scope\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003EConfiguration \u0026amp; Extensibility\u003C/strong\u003E\u003C/p\u003E\n" + "Html": "\n\u003Cp\u003EIn UltimateAuth, devices are not an afterthought.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 They are a \u003Cstrong\u003Efirst-class concept\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022why-device-matters\u0022\u003E\uD83E\uDDE0 Why Device Matters\u003C/h2\u003E\n\u003Cp\u003EMost authentication systems ignore devices.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EA user logs in\u003C/li\u003E\n\u003Cli\u003EA token is issued\u003C/li\u003E\n\u003Cli\u003EEverything is treated the same\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This breaks down when you need:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EMulti-device control\u003C/li\u003E\n\u003Cli\u003ESession visibility\u003C/li\u003E\n\u003Cli\u003ESecurity enforcement\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 UltimateAuth solves this with \u003Cstrong\u003Edevice-aware authentication\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022core-concept-chain-device\u0022\u003E\uD83E\uDDE9 Core Concept: Chain = Device\u003C/h2\u003E\n\u003Cp\u003EIn UltimateAuth:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 A \u003Cstrong\u003ESessionChain represents a device\u003C/strong\u003E\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003EDevice \u2192 Chain \u2192 Sessions\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EEach chain:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EIs bound to a device\u003C/li\u003E\n\u003Cli\u003EGroups sessions\u003C/li\u003E\n\u003Cli\u003ETracks activity\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 A device is not inferred \u2014 it is explicitly modeled\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-defines-a-device\u0022\u003E\uD83D\uDD17 What Defines a Device?\u003C/h2\u003E\n\u003Cp\u003EA chain includes:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EDeviceId\u003C/li\u003E\n\u003Cli\u003EPlatform (web, mobile, etc.)\u003C/li\u003E\n\u003Cli\u003EOperating System\u003C/li\u003E\n\u003Cli\u003EBrowser\u003C/li\u003E\n\u003Cli\u003EIP (optional binding)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This forms a \u003Cstrong\u003Edevice fingerprint\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022device-lifecycle\u0022\u003E\uD83D\uDD04 Device Lifecycle\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022first-login\u0022\u003E1\uFE0F\u20E3 First Login\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ENew device detected\u003C/li\u003E\n\u003Cli\u003ENew chain is created\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022subsequent-logins\u0022\u003E2\uFE0F\u20E3 Subsequent Logins\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ESame device \u2192 reuse chain\u003C/li\u003E\n\u003Cli\u003ENew device \u2192 new chain\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Device continuity is preserved\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022activity-touch\u0022\u003E3\uFE0F\u20E3 Activity (Touch)\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EChain \u003Ccode\u003ELastSeenAt\u003C/code\u003E updated\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003ETouchCount\u003C/code\u003E increases\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Tracks real usage\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022token-rotation\u0022\u003E4\uFE0F\u20E3 Token Rotation\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession changes\u003C/li\u003E\n\u003Cli\u003EChain remains\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003ERotationCount\u003C/code\u003E increases\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Device identity stays stable\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022logout\u0022\u003E5\uFE0F\u20E3 Logout\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession removed\u003C/li\u003E\n\u003Cli\u003EChain remains\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Device still trusted\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022revoke\u0022\u003E6\uFE0F\u20E3 Revoke\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EChain invalidated\u003C/li\u003E\n\u003Cli\u003EAll sessions removed\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Device trust is reset\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-model\u0022\u003E\uD83D\uDD10 Security Model\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022device-binding\u0022\u003E\uD83D\uDD17 Device Binding\u003C/h3\u003E\n\u003Cp\u003ESessions and tokens are tied to:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EChain\u003C/li\u003E\n\u003Cli\u003EDevice context\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Prevents cross-device reuse\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022rotation-tracking\u0022\u003E\uD83D\uDD01 Rotation Tracking\u003C/h3\u003E\n\u003Cp\u003EChains track:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERotationCount\u003C/li\u003E\n\u003Cli\u003ETouchCount\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Enables anomaly detection\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022revoke-cascade\u0022\u003E\uD83D\uDEA8 Revoke Cascade\u003C/h3\u003E\n\u003Cp\u003EIf a device is compromised:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEntire chain can be revoked\u003C/li\u003E\n\u003Cli\u003EAll sessions invalidated\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Immediate containment\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022configuration\u0022\u003E\u2699\uFE0F Configuration\u003C/h2\u003E\n\u003Cp\u003EDevice behavior is configurable via session options:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EMax chains per user\u003C/li\u003E\n\u003Cli\u003EMax sessions per chain\u003C/li\u003E\n\u003Cli\u003EPlatform-based limits\u003C/li\u003E\n\u003Cli\u003EDevice mismatch behavior\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Fine-grained control for enterprise scenarios\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Device = Chain\u003Cbr /\u003E\n\uD83D\uDC49 Not just metadata\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EDevices are explicitly modeled\u003C/li\u003E\n\u003Cli\u003EEach device has its own chain\u003C/li\u003E\n\u003Cli\u003ESessions belong to chains\u003C/li\u003E\n\u003Cli\u003ESecurity is enforced per device\u003C/li\u003E\n\u003Cli\u003ELogout and revoke operate on device scope\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003EConfiguration \u0026amp; Extensibility\u003C/strong\u003E\u003C/p\u003E\n", + "Headings": [ + { + "Id": "why-device-matters", + "Text": "\uD83E\uDDE0 Why Device Matters", + "Level": 0 + }, + { + "Id": "core-concept-chain-device", + "Text": "\uD83E\uDDE9 Core Concept: Chain = Device", + "Level": 0 + }, + { + "Id": "what-defines-a-device", + "Text": "\uD83D\uDD17 What Defines a Device?", + "Level": 0 + }, + { + "Id": "device-lifecycle", + "Text": "\uD83D\uDD04 Device Lifecycle", + "Level": 0 + }, + { + "Id": "first-login", + "Text": "1\uFE0F\u20E3 First Login", + "Level": 1 + }, + { + "Id": "subsequent-logins", + "Text": "2\uFE0F\u20E3 Subsequent Logins", + "Level": 1 + }, + { + "Id": "activity-touch", + "Text": "3\uFE0F\u20E3 Activity (Touch)", + "Level": 1 + }, + { + "Id": "token-rotation", + "Text": "4\uFE0F\u20E3 Token Rotation", + "Level": 1 + }, + { + "Id": "logout", + "Text": "5\uFE0F\u20E3 Logout", + "Level": 1 + }, + { + "Id": "revoke", + "Text": "6\uFE0F\u20E3 Revoke", + "Level": 1 + }, + { + "Id": "security-model", + "Text": "\uD83D\uDD10 Security Model", + "Level": 0 + }, + { + "Id": "device-binding", + "Text": "\uD83D\uDD17 Device Binding", + "Level": 1 + }, + { + "Id": "rotation-tracking", + "Text": "\uD83D\uDD01 Rotation Tracking", + "Level": 1 + }, + { + "Id": "revoke-cascade", + "Text": "\uD83D\uDEA8 Revoke Cascade", + "Level": 1 + }, + { + "Id": "configuration", + "Text": "\u2699\uFE0F Configuration", + "Level": 0 + }, + { + "Id": "mental-model", + "Text": "\uD83E\uDDE0 Mental Model", + "Level": 0 + }, + { + "Id": "key-takeaways", + "Text": "\uD83D\uDCCC Key Takeaways", + "Level": 0 + }, + { + "Id": "next-step", + "Text": "\u27A1\uFE0F Next Step", + "Level": 0 + } + ] } \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/index.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/index.json index 15fe6ca3..bb7849a2 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/index.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/index.json @@ -1,5 +1,77 @@ { "Slug": "auth-flows/index", "Title": "Auth Flows", - "Html": "\n\u003Cp\u003EAuthentication in UltimateAuth is not a single operation.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 It is a \u003Cstrong\u003Eflow-driven system\u003C/strong\u003E.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022what-is-an-auth-flow\u0022\u003E\uD83E\uDDE0 What is an Auth Flow?\u003C/h2\u003E\n\u003Cp\u003EAn auth flow represents a complete authentication operation, such as:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogging in\u003C/li\u003E\n\u003Cli\u003ERefreshing a session\u003C/li\u003E\n\u003Cli\u003ELogging out\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EEach flow:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EHas a defined lifecycle\u003C/li\u003E\n\u003Cli\u003ERuns through the orchestration pipeline\u003C/li\u003E\n\u003Cli\u003EProduces a controlled authentication outcome\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Instead of calling isolated APIs, you execute \u003Cstrong\u003Eflows\u003C/strong\u003E.\u003C/p\u003E\n\u003Ch2 id=\u0022why-flow-based\u0022\u003E\uD83D\uDD04 Why Flow-Based?\u003C/h2\u003E\n\u003Cp\u003ETraditional systems treat authentication as:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EA login endpoint\u003C/li\u003E\n\u003Cli\u003EA token generator\u003C/li\u003E\n\u003Cli\u003EA cookie setter\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 These approaches often lead to fragmented logic.\u003C/p\u003E\n\u003Cp\u003EUltimateAuth solves this by:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EStructuring authentication as flows\u003C/li\u003E\n\u003Cli\u003EEnforcing a consistent execution model\u003C/li\u003E\n\u003Cli\u003ECentralizing security decisions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022what-happens-during-a-flow\u0022\u003E\uD83E\uDDE9 What Happens During a Flow?\u003C/h2\u003E\n\u003Cp\u003EEvery flow follows the same pattern:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003EFlow \u2192 Context \u2192 Orchestrator \u2192 Authority \u2192 Result\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cul\u003E\n\u003Cli\u003EThe \u003Cstrong\u003Eflow\u003C/strong\u003E defines the intent\u003C/li\u003E\n\u003Cli\u003EThe \u003Cstrong\u003Econtext\u003C/strong\u003E carries state\u003C/li\u003E\n\u003Cli\u003EThe \u003Cstrong\u003Eorchestrator\u003C/strong\u003E coordinates execution\u003C/li\u003E\n\u003Cli\u003EThe \u003Cstrong\u003Eauthority\u003C/strong\u003E enforces rules\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This ensures consistent and secure behavior across all operations.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022types-of-flows\u0022\u003E\uD83D\uDD10 Types of Flows\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth provides built-in flows for common scenarios:\u003C/p\u003E\n\u003Ch3 id=\u0022login-flow\u0022\u003E\uD83D\uDD11 Login Flow\u003C/h3\u003E\n\u003Cp\u003EEstablishes authentication by:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EValidating credentials\u003C/li\u003E\n\u003Cli\u003ECreating session hierarchy (root, chain, session)\u003C/li\u003E\n\u003Cli\u003EIssuing tokens if required\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ca href=\u0022./login-flow.md\u0022\u003ELearn more\u003C/a\u003E\u003C/p\u003E\n\u003Ch3 id=\u0022refresh-flow\u0022\u003E\uD83D\uDD04 Refresh Flow\u003C/h3\u003E\n\u003Cp\u003EExtends an existing session:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERotates refresh tokens\u003C/li\u003E\n\u003Cli\u003EMaintains session continuity\u003C/li\u003E\n\u003Cli\u003EApplies sliding expiration\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ca href=\u0022./refresh-flow.md\u0022\u003ELearn more\u003C/a\u003E\u003C/p\u003E\n\u003Ch3 id=\u0022logout-flow\u0022\u003E\uD83D\uDEAA Logout Flow\u003C/h3\u003E\n\u003Cp\u003ETerminates authentication:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERevokes session(s)\u003C/li\u003E\n\u003Cli\u003EInvalidates tokens\u003C/li\u003E\n\u003Cli\u003ESupports device-level or global logout\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ca href=\u0022./logout-flow.md\u0022\u003ELearn more\u003C/a\u003E\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022supporting-concepts\u0022\u003E\uD83E\uDDE0 Supporting Concepts\u003C/h2\u003E\n\u003Cp\u003EThese flows operate on top of deeper system models:\u003C/p\u003E\n\u003Ch3 id=\u0022session-lifecycle\u0022\u003E\uD83E\uDDEC Session Lifecycle\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ERoot \u2192 Chain \u2192 Session hierarchy\u003C/li\u003E\n\u003Cli\u003EDevice-aware session structure\u003C/li\u003E\n\u003Cli\u003ELifecycle management and revocation\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ca href=\u0022./session-lifecycle.md\u0022\u003ELearn more\u003C/a\u003E\u003C/p\u003E\n\u003Ch3 id=\u0022token-behavior\u0022\u003E\uD83C\uDF9F Token Behavior\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EAccess token vs refresh token\u003C/li\u003E\n\u003Cli\u003EOpaque vs JWT\u003C/li\u003E\n\u003Cli\u003EMode-dependent behavior\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ca href=\u0022./token-behavior.md\u0022\u003ELearn more\u003C/a\u003E\u003C/p\u003E\n\u003Ch3 id=\u0022device-management\u0022\u003E\uD83D\uDCF1 Device Management\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EDevice binding\u003C/li\u003E\n\u003Cli\u003EMulti-device sessions\u003C/li\u003E\n\u003Cli\u003ESecurity implications\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ca href=\u0022./device-management.md\u0022\u003ELearn more\u003C/a\u003E\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Cstrong\u003EAuthentication is not a single step\u003C/strong\u003E\u003Cbr /\u003E\n\uD83D\uDC49 \u003Cstrong\u003EIt is a controlled flow of state transitions\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EAuthentication is executed as flows\u003C/li\u003E\n\u003Cli\u003EEach flow follows a consistent pipeline\u003C/li\u003E\n\u003Cli\u003ESessions and tokens are created as part of flows\u003C/li\u003E\n\u003Cli\u003ESecurity is enforced centrally\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Ch2 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EStart with the most important flow:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Continue to \u003Cstrong\u003ELogin Flow\u003C/strong\u003E\u003C/p\u003E\n" + "Html": "\n\u003Cp\u003EAuthentication in UltimateAuth is not a single operation.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 It is a \u003Cstrong\u003Eflow-driven system\u003C/strong\u003E.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-is-an-auth-flow\u0022\u003E\uD83E\uDDE0 What is an Auth Flow?\u003C/h2\u003E\n\u003Cp\u003EAn auth flow represents a complete authentication operation, such as:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogging in\u003C/li\u003E\n\u003Cli\u003ERefreshing a session\u003C/li\u003E\n\u003Cli\u003ELogging out\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EEach flow:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EHas a defined lifecycle\u003C/li\u003E\n\u003Cli\u003ERuns through the orchestration pipeline\u003C/li\u003E\n\u003Cli\u003EProduces a controlled authentication outcome\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Instead of calling isolated APIs, you execute \u003Cstrong\u003Eflows\u003C/strong\u003E.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022why-flow-based\u0022\u003E\uD83D\uDD04 Why Flow-Based?\u003C/h2\u003E\n\u003Cp\u003ETraditional systems treat authentication as:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EA login endpoint\u003C/li\u003E\n\u003Cli\u003EA token generator\u003C/li\u003E\n\u003Cli\u003EA cookie setter\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 These approaches often lead to fragmented logic.\u003C/p\u003E\n\u003Cp\u003EUltimateAuth solves this by:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EStructuring authentication as flows\u003C/li\u003E\n\u003Cli\u003EEnforcing a consistent execution model\u003C/li\u003E\n\u003Cli\u003ECentralizing security decisions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-happens-during-a-flow\u0022\u003E\uD83E\uDDE9 What Happens During a Flow?\u003C/h2\u003E\n\u003Cp\u003EEvery flow follows the same pattern:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003EFlow \u2192 Context \u2192 Orchestrator \u2192 Authority \u2192 Result\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cul\u003E\n\u003Cli\u003EThe \u003Cstrong\u003Eflow\u003C/strong\u003E defines the intent\u003C/li\u003E\n\u003Cli\u003EThe \u003Cstrong\u003Econtext\u003C/strong\u003E carries state\u003C/li\u003E\n\u003Cli\u003EThe \u003Cstrong\u003Eorchestrator\u003C/strong\u003E coordinates execution\u003C/li\u003E\n\u003Cli\u003EThe \u003Cstrong\u003Eauthority\u003C/strong\u003E enforces rules\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This ensures consistent and secure behavior across all operations.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022types-of-flows\u0022\u003E\uD83D\uDD10 Types of Flows\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth provides built-in flows for common scenarios:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022login-flow\u0022\u003E\uD83D\uDD11 Login Flow\u003C/h3\u003E\n\u003Cp\u003EEstablishes authentication by:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EValidating credentials\u003C/li\u003E\n\u003Cli\u003ECreating session hierarchy (root, chain, session)\u003C/li\u003E\n\u003Cli\u003EIssuing tokens if required\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ca href=\u0022./login-flow.md\u0022\u003ELearn more\u003C/a\u003E\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022refresh-flow\u0022\u003E\uD83D\uDD04 Refresh Flow\u003C/h3\u003E\n\u003Cp\u003EExtends an existing session:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERotates refresh tokens\u003C/li\u003E\n\u003Cli\u003EMaintains session continuity\u003C/li\u003E\n\u003Cli\u003EApplies sliding expiration\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ca href=\u0022./refresh-flow.md\u0022\u003ELearn more\u003C/a\u003E\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022logout-flow\u0022\u003E\uD83D\uDEAA Logout Flow\u003C/h3\u003E\n\u003Cp\u003ETerminates authentication:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERevokes session(s)\u003C/li\u003E\n\u003Cli\u003EInvalidates tokens\u003C/li\u003E\n\u003Cli\u003ESupports device-level or global logout\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ca href=\u0022./logout-flow.md\u0022\u003ELearn more\u003C/a\u003E\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022supporting-concepts\u0022\u003E\uD83E\uDDE0 Supporting Concepts\u003C/h2\u003E\n\u003Cp\u003EThese flows operate on top of deeper system models:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022session-lifecycle\u0022\u003E\uD83E\uDDEC Session Lifecycle\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ERoot \u2192 Chain \u2192 Session hierarchy\u003C/li\u003E\n\u003Cli\u003EDevice-aware session structure\u003C/li\u003E\n\u003Cli\u003ELifecycle management and revocation\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ca href=\u0022./session-lifecycle.md\u0022\u003ELearn more\u003C/a\u003E\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022token-behavior\u0022\u003E\uD83C\uDF9F Token Behavior\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EAccess token vs refresh token\u003C/li\u003E\n\u003Cli\u003EOpaque vs JWT\u003C/li\u003E\n\u003Cli\u003EMode-dependent behavior\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ca href=\u0022./token-behavior.md\u0022\u003ELearn more\u003C/a\u003E\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022device-management\u0022\u003E\uD83D\uDCF1 Device Management\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EDevice binding\u003C/li\u003E\n\u003Cli\u003EMulti-device sessions\u003C/li\u003E\n\u003Cli\u003ESecurity implications\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ca href=\u0022./device-management.md\u0022\u003ELearn more\u003C/a\u003E\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Cstrong\u003EAuthentication is not a single step\u003C/strong\u003E\u003Cbr /\u003E\n\uD83D\uDC49 \u003Cstrong\u003EIt is a controlled flow of state transitions\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EAuthentication is executed as flows\u003C/li\u003E\n\u003Cli\u003EEach flow follows a consistent pipeline\u003C/li\u003E\n\u003Cli\u003ESessions and tokens are created as part of flows\u003C/li\u003E\n\u003Cli\u003ESecurity is enforced centrally\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EStart with the most important flow:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Continue to \u003Cstrong\u003ELogin Flow\u003C/strong\u003E\u003C/p\u003E\n", + "Headings": [ + { + "Id": "what-is-an-auth-flow", + "Text": "\uD83E\uDDE0 What is an Auth Flow?", + "Level": 0 + }, + { + "Id": "why-flow-based", + "Text": "\uD83D\uDD04 Why Flow-Based?", + "Level": 0 + }, + { + "Id": "what-happens-during-a-flow", + "Text": "\uD83E\uDDE9 What Happens During a Flow?", + "Level": 0 + }, + { + "Id": "types-of-flows", + "Text": "\uD83D\uDD10 Types of Flows", + "Level": 0 + }, + { + "Id": "login-flow", + "Text": "\uD83D\uDD11 Login Flow", + "Level": 1 + }, + { + "Id": "refresh-flow", + "Text": "\uD83D\uDD04 Refresh Flow", + "Level": 1 + }, + { + "Id": "logout-flow", + "Text": "\uD83D\uDEAA Logout Flow", + "Level": 1 + }, + { + "Id": "supporting-concepts", + "Text": "\uD83E\uDDE0 Supporting Concepts", + "Level": 0 + }, + { + "Id": "session-lifecycle", + "Text": "\uD83E\uDDEC Session Lifecycle", + "Level": 1 + }, + { + "Id": "token-behavior", + "Text": "\uD83C\uDF9F Token Behavior", + "Level": 1 + }, + { + "Id": "device-management", + "Text": "\uD83D\uDCF1 Device Management", + "Level": 1 + }, + { + "Id": "mental-model", + "Text": "\uD83E\uDDE0 Mental Model", + "Level": 0 + }, + { + "Id": "key-takeaways", + "Text": "\uD83D\uDCCC Key Takeaways", + "Level": 0 + }, + { + "Id": "next-step", + "Text": "\u27A1\uFE0F Next Step", + "Level": 0 + } + ] } \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/login-flow.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/login-flow.json index 171f28fc..497b1331 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/login-flow.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/login-flow.json @@ -1,5 +1,102 @@ { "Slug": "auth-flows/login-flow", "Title": "Login", - "Html": "\n\u003Cp\u003EThe login flow in UltimateAuth is not just credential validation.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 It is a \u003Cstrong\u003Econtrolled session establishment process\u003C/strong\u003E.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022what-is-login\u0022\u003E\uD83E\uDDE0 What is Login?\u003C/h2\u003E\n\u003Cp\u003EIn traditional systems:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EValidate credentials\u003C/li\u003E\n\u003Cli\u003EIssue a token\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EIn UltimateAuth:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Login creates a \u003Cstrong\u003Esession hierarchy\u003C/strong\u003E\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003ERoot \u2192 Chain \u2192 Session\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003ELogin does not create a token\u003Cbr /\u003E\n\u2192 It creates a session\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cp\u003ETokens are optional outputs derived from the session.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022step-by-step-execution\u0022\u003E\uD83D\uDD04 Step-by-Step Execution\u003C/h2\u003E\n\u003Cp\u003EThe login flow follows a structured pipeline:\u003C/p\u003E\n\u003Ch3 id=\u0022identifier-resolution\u0022\u003E1\uFE0F\u20E3 Identifier Resolution\u003C/h3\u003E\n\u003Cp\u003EThe system resolves the user identity:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUsername / email / phone \u2192 \u003Ccode\u003EUserKey\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022user-security-state\u0022\u003E2\uFE0F\u20E3 User \u0026amp; Security State\u003C/h3\u003E\n\u003Cp\u003EThe system loads:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUser existence\u003C/li\u003E\n\u003Cli\u003EAccount state\u003C/li\u003E\n\u003Cli\u003EFactor (credential) state\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EChecks include:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EIs the account locked?\u003C/li\u003E\n\u003Cli\u003EIs reauthentication required?\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022credential-validation\u0022\u003E3\uFE0F\u20E3 Credential Validation\u003C/h3\u003E\n\u003Cp\u003ECredentials are validated using providers:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EPassword\u003C/li\u003E\n\u003Cli\u003E(Extensible: OTP, external providers, etc.)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022authority-decision\u0022\u003E4\uFE0F\u20E3 Authority Decision\u003C/h3\u003E\n\u003Cp\u003EThe \u003Cstrong\u003ELoginAuthority\u003C/strong\u003E evaluates the attempt.\u003C/p\u003E\n\u003Cp\u003EPossible outcomes:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u2705 Allow\u003C/li\u003E\n\u003Cli\u003E\u274C Deny\u003C/li\u003E\n\u003Cli\u003E\u26A0\uFE0F Challenge (e.g. MFA)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 No session is created before this decision.\u003C/p\u003E\n\u003Ch3 id=\u0022device-chain-resolution\u0022\u003E5\uFE0F\u20E3 Device \u0026amp; Chain Resolution\u003C/h3\u003E\n\u003Cp\u003EThe system checks if the device is known:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EExisting device \u2192 reuse chain\u003C/li\u003E\n\u003Cli\u003ENew device \u2192 create new chain\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 A \u003Cstrong\u003EChain represents a device\u003C/strong\u003E\u003C/p\u003E\n\u003Ch3 id=\u0022session-creation\u0022\u003E6\uFE0F\u20E3 Session Creation\u003C/h3\u003E\n\u003Cp\u003EA new session is issued:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELinked to user\u003C/li\u003E\n\u003Cli\u003ELinked to device (chain)\u003C/li\u003E\n\u003Cli\u003EBound to tenant\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003ESession hierarchy:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003EUser \u2192 Root \u2192 Chain \u2192 Session\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022token-issuance-optional\u0022\u003E7\uFE0F\u20E3 Token Issuance (Optional)\u003C/h3\u003E\n\u003Cp\u003EDepending on the mode and request:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAccess token may be issued\u003C/li\u003E\n\u003Cli\u003ERefresh token may be issued\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Tokens are derived from the session\u003Cbr /\u003E\n\uD83D\uDC49 Not the source of truth\u003C/p\u003E\n\u003Ch3 id=\u0022event-dispatch\u0022\u003E8\uFE0F\u20E3 Event Dispatch\u003C/h3\u003E\n\u003Cp\u003EThe system emits:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogin events\u003C/li\u003E\n\u003Cli\u003EAudit information\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022what-gets-created\u0022\u003E\uD83E\uDDE9 What Gets Created?\u003C/h2\u003E\n\u003Cp\u003EA successful login creates:\u003C/p\u003E\n\u003Ch3 id=\u0022root\u0022\u003E\uD83D\uDD39 Root\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EOne per user\u003C/li\u003E\n\u003Cli\u003ERepresents global authentication state\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022chain\u0022\u003E\uD83D\uDD39 Chain\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EOne per device\u003C/li\u003E\n\u003Cli\u003EManages device lifecycle\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022session\u0022\u003E\uD83D\uDD39 Session\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EIndividual authentication instance\u003C/li\u003E\n\u003Cli\u003ERepresents a single login\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022device-awareness\u0022\u003E\uD83D\uDCF1 Device Awareness\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth is device-aware by design:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEach device gets its own chain\u003C/li\u003E\n\u003Cli\u003ESessions are grouped by device\u003C/li\u003E\n\u003Cli\u003ELogout can target device or all sessions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022security-considerations\u0022\u003E\uD83D\uDD10 Security Considerations\u003C/h2\u003E\n\u003Cp\u003EThe login flow includes built-in protections:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAccount lockout\u003C/li\u003E\n\u003Cli\u003EFailed attempt tracking\u003C/li\u003E\n\u003Cli\u003EDevice binding\u003C/li\u003E\n\u003Cli\u003ESecurity version validation\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Security decisions are centralized in the Authority\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Login = session creation\u003Cbr /\u003E\n\uD83D\uDC49 Not token issuance\u003C/p\u003E\n\u003Ch2 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogin is a flow, not a function\u003C/li\u003E\n\u003Cli\u003EAuthority decides before any state change\u003C/li\u003E\n\u003Cli\u003ESessions are the source of truth\u003C/li\u003E\n\u003Cli\u003ETokens are optional representations\u003C/li\u003E\n\u003Cli\u003EDevice context is always considered\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003ERefresh Flow\u003C/strong\u003E\u003C/p\u003E\n" + "Html": "\n\u003Cp\u003EThe login flow in UltimateAuth is not just credential validation.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 It is a \u003Cstrong\u003Econtrolled session establishment process\u003C/strong\u003E.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-is-login\u0022\u003E\uD83E\uDDE0 What is Login?\u003C/h2\u003E\n\u003Cp\u003EIn traditional systems:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EValidate credentials\u003C/li\u003E\n\u003Cli\u003EIssue a token\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EIn UltimateAuth:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Login creates a \u003Cstrong\u003Esession hierarchy\u003C/strong\u003E\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003ERoot \u2192 Chain \u2192 Session\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003ELogin does not create a token\u003Cbr /\u003E\n\u2192 It creates a session\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cp\u003ETokens are optional outputs derived from the session.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022step-by-step-execution\u0022\u003E\uD83D\uDD04 Step-by-Step Execution\u003C/h2\u003E\n\u003Cp\u003EThe login flow follows a structured pipeline:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022identifier-resolution\u0022\u003E1\uFE0F\u20E3 Identifier Resolution\u003C/h3\u003E\n\u003Cp\u003EThe system resolves the user identity:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUsername / email / phone \u2192 \u003Ccode\u003EUserKey\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022user-security-state\u0022\u003E2\uFE0F\u20E3 User \u0026amp; Security State\u003C/h3\u003E\n\u003Cp\u003EThe system loads:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUser existence\u003C/li\u003E\n\u003Cli\u003EAccount state\u003C/li\u003E\n\u003Cli\u003EFactor (credential) state\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EChecks include:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EIs the account locked?\u003C/li\u003E\n\u003Cli\u003EIs reauthentication required?\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022credential-validation\u0022\u003E3\uFE0F\u20E3 Credential Validation\u003C/h3\u003E\n\u003Cp\u003ECredentials are validated using providers:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EPassword\u003C/li\u003E\n\u003Cli\u003E(Extensible: OTP, external providers, etc.)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022authority-decision\u0022\u003E4\uFE0F\u20E3 Authority Decision\u003C/h3\u003E\n\u003Cp\u003EThe \u003Cstrong\u003ELoginAuthority\u003C/strong\u003E evaluates the attempt.\u003C/p\u003E\n\u003Cp\u003EPossible outcomes:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u2705 Allow\u003C/li\u003E\n\u003Cli\u003E\u274C Deny\u003C/li\u003E\n\u003Cli\u003E\u26A0\uFE0F Challenge (e.g. MFA)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 No session is created before this decision.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022device-chain-resolution\u0022\u003E5\uFE0F\u20E3 Device \u0026amp; Chain Resolution\u003C/h3\u003E\n\u003Cp\u003EThe system checks if the device is known:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EExisting device \u2192 reuse chain\u003C/li\u003E\n\u003Cli\u003ENew device \u2192 create new chain\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 A \u003Cstrong\u003EChain represents a device\u003C/strong\u003E\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022session-creation\u0022\u003E6\uFE0F\u20E3 Session Creation\u003C/h3\u003E\n\u003Cp\u003EA new session is issued:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELinked to user\u003C/li\u003E\n\u003Cli\u003ELinked to device (chain)\u003C/li\u003E\n\u003Cli\u003EBound to tenant\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003ESession hierarchy:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003EUser \u2192 Root \u2192 Chain \u2192 Session\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022token-issuance-optional\u0022\u003E7\uFE0F\u20E3 Token Issuance (Optional)\u003C/h3\u003E\n\u003Cp\u003EDepending on the mode and request:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAccess token may be issued\u003C/li\u003E\n\u003Cli\u003ERefresh token may be issued\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Tokens are derived from the session\u003Cbr /\u003E\n\uD83D\uDC49 Not the source of truth\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022event-dispatch\u0022\u003E8\uFE0F\u20E3 Event Dispatch\u003C/h3\u003E\n\u003Cp\u003EThe system emits:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogin events\u003C/li\u003E\n\u003Cli\u003EAudit information\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-gets-created\u0022\u003E\uD83E\uDDE9 What Gets Created?\u003C/h2\u003E\n\u003Cp\u003EA successful login creates:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022root\u0022\u003E\uD83D\uDD39 Root\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EOne per user\u003C/li\u003E\n\u003Cli\u003ERepresents global authentication state\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022chain\u0022\u003E\uD83D\uDD39 Chain\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EOne per device\u003C/li\u003E\n\u003Cli\u003EManages device lifecycle\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022session\u0022\u003E\uD83D\uDD39 Session\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EIndividual authentication instance\u003C/li\u003E\n\u003Cli\u003ERepresents a single login\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022device-awareness\u0022\u003E\uD83D\uDCF1 Device Awareness\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth is device-aware by design:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEach device gets its own chain\u003C/li\u003E\n\u003Cli\u003ESessions are grouped by device\u003C/li\u003E\n\u003Cli\u003ELogout can target device or all sessions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-considerations\u0022\u003E\uD83D\uDD10 Security Considerations\u003C/h2\u003E\n\u003Cp\u003EThe login flow includes built-in protections:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAccount lockout\u003C/li\u003E\n\u003Cli\u003EFailed attempt tracking\u003C/li\u003E\n\u003Cli\u003EDevice binding\u003C/li\u003E\n\u003Cli\u003ESecurity version validation\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Security decisions are centralized in the Authority\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Login = session creation\u003Cbr /\u003E\n\uD83D\uDC49 Not token issuance\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogin is a flow, not a function\u003C/li\u003E\n\u003Cli\u003EAuthority decides before any state change\u003C/li\u003E\n\u003Cli\u003ESessions are the source of truth\u003C/li\u003E\n\u003Cli\u003ETokens are optional representations\u003C/li\u003E\n\u003Cli\u003EDevice context is always considered\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003ERefresh Flow\u003C/strong\u003E\u003C/p\u003E\n", + "Headings": [ + { + "Id": "what-is-login", + "Text": "\uD83E\uDDE0 What is Login?", + "Level": 0 + }, + { + "Id": "step-by-step-execution", + "Text": "\uD83D\uDD04 Step-by-Step Execution", + "Level": 0 + }, + { + "Id": "identifier-resolution", + "Text": "1\uFE0F\u20E3 Identifier Resolution", + "Level": 1 + }, + { + "Id": "user-security-state", + "Text": "2\uFE0F\u20E3 User \u0026 Security State", + "Level": 1 + }, + { + "Id": "credential-validation", + "Text": "3\uFE0F\u20E3 Credential Validation", + "Level": 1 + }, + { + "Id": "authority-decision", + "Text": "4\uFE0F\u20E3 Authority Decision", + "Level": 1 + }, + { + "Id": "device-chain-resolution", + "Text": "5\uFE0F\u20E3 Device \u0026 Chain Resolution", + "Level": 1 + }, + { + "Id": "session-creation", + "Text": "6\uFE0F\u20E3 Session Creation", + "Level": 1 + }, + { + "Id": "token-issuance-optional", + "Text": "7\uFE0F\u20E3 Token Issuance (Optional)", + "Level": 1 + }, + { + "Id": "event-dispatch", + "Text": "8\uFE0F\u20E3 Event Dispatch", + "Level": 1 + }, + { + "Id": "what-gets-created", + "Text": "\uD83E\uDDE9 What Gets Created?", + "Level": 0 + }, + { + "Id": "root", + "Text": "\uD83D\uDD39 Root", + "Level": 1 + }, + { + "Id": "chain", + "Text": "\uD83D\uDD39 Chain", + "Level": 1 + }, + { + "Id": "session", + "Text": "\uD83D\uDD39 Session", + "Level": 1 + }, + { + "Id": "device-awareness", + "Text": "\uD83D\uDCF1 Device Awareness", + "Level": 0 + }, + { + "Id": "security-considerations", + "Text": "\uD83D\uDD10 Security Considerations", + "Level": 0 + }, + { + "Id": "mental-model", + "Text": "\uD83E\uDDE0 Mental Model", + "Level": 0 + }, + { + "Id": "key-takeaways", + "Text": "\uD83D\uDCCC Key Takeaways", + "Level": 0 + }, + { + "Id": "next-step", + "Text": "\u27A1\uFE0F Next Step", + "Level": 0 + } + ] } \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/logout-flow.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/logout-flow.json index 698ea024..4d17bcda 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/logout-flow.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/logout-flow.json @@ -1,5 +1,122 @@ { "Slug": "auth-flows/logout-flow", "Title": "Logout", - "Html": "\n\u003Cp\u003EThe logout flow in UltimateAuth is not a single action.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 It represents different \u003Cstrong\u003Elevels of authentication termination\u003C/strong\u003E.\u003C/p\u003E\n\u003Ch2 id=\u0022what-is-logout\u0022\u003E\uD83E\uDDE0 What is Logout?\u003C/h2\u003E\n\u003Cp\u003EIn traditional systems:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogout = remove cookie or token\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EIn UltimateAuth:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Logout affects \u003Cstrong\u003Esession, device, or identity scope\u003C/strong\u003E\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003ELogout is not just removing access\u003Cbr /\u003E\n\u2192 It is controlling session lifecycle\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Ch2 id=\u0022logout-vs-revoke\u0022\u003E\uD83D\uDD00 Logout vs Revoke\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth distinguishes between two concepts:\u003C/p\u003E\n\u003Ch3 id=\u0022logout-soft-termination\u0022\u003E\uD83D\uDD39 Logout (Soft Termination)\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EEnds the current session\u003C/li\u003E\n\u003Cli\u003EKeeps the device (chain) active\u003C/li\u003E\n\u003Cli\u003EAllows re-login without resetting device context\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cpre\u003E\u003Ccode\u003ESession \u2192 Invalidated\nChain \u2192 Still Active\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 User can log in again and continue on the same device chain\u003C/p\u003E\n\u003Ch3 id=\u0022revoke-hard-invalidation\u0022\u003E\uD83D\uDD25 Revoke (Hard Invalidation)\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EInvalidates session, chain, or root\u003C/li\u003E\n\u003Cli\u003ECannot be undone\u003C/li\u003E\n\u003Cli\u003EForces a completely new authentication path\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cpre\u003E\u003Ccode\u003EChain \u2192 Revoked\nSessions \u2192 Revoked\nNext login \u2192 New chain\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Revoke resets trust for that scope\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022levels-of-termination\u0022\u003E\uD83E\uDDE9 Levels of Termination\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth supports multiple logout scopes:\u003C/p\u003E\n\u003Ch3 id=\u0022session-level-logout\u0022\u003E\uD83D\uDD39 Session-Level Logout\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ETerminates a single session\u003C/li\u003E\n\u003Cli\u003EOther sessions on the same device may remain\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022device-level-chain\u0022\u003E\uD83D\uDCF1 Device-Level (Chain)\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ETerminates all sessions on a device\u003C/li\u003E\n\u003Cli\u003EDevice chain is invalidated or reset\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022global-logout-all-devices\u0022\u003E\uD83C\uDF10 Global Logout (All Devices)\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ETerminates all sessions across all devices\u003C/li\u003E\n\u003Cli\u003EKeeps root (user identity) intact\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022root-revoke\u0022\u003E\uD83D\uDD25 Root Revoke\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EInvalidates entire authentication state\u003C/li\u003E\n\u003Cli\u003EAll chains and sessions are revoked\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This is the strongest possible action\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022step-by-step-execution\u0022\u003E\uD83D\uDD04 Step-by-Step Execution\u003C/h2\u003E\n\u003Ch3 id=\u0022flow-context-resolution\u0022\u003E1\uFE0F\u20E3 Flow Context Resolution\u003C/h3\u003E\n\u003Cp\u003EThe system resolves:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECurrent session\u003C/li\u003E\n\u003Cli\u003EUser identity\u003C/li\u003E\n\u003Cli\u003ETenant\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022authority-decision\u0022\u003E2\uFE0F\u20E3 Authority Decision\u003C/h3\u003E\n\u003Cp\u003ELogout operations are validated:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAuthorization checks\u003C/li\u003E\n\u003Cli\u003EAccess validation\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Logout is not blindly executed\u003C/p\u003E\n\u003Ch3 id=\u0022scope-determination\u0022\u003E3\uFE0F\u20E3 Scope Determination\u003C/h3\u003E\n\u003Cp\u003EThe system determines what to terminate:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession\u003C/li\u003E\n\u003Cli\u003EChain\u003C/li\u003E\n\u003Cli\u003ERoot\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022execution\u0022\u003E4\uFE0F\u20E3 Execution\u003C/h3\u003E\n\u003Cp\u003EDepending on scope:\u003C/p\u003E\n\u003Ch4 id=\u0022session-logout\u0022\u003ESession Logout\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession is revoked\u003C/li\u003E\n\u003Cli\u003EOther sessions unaffected\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch4 id=\u0022chain-revoke-logout\u0022\u003EChain Revoke / Logout\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003EAll sessions in the chain are revoked\u003C/li\u003E\n\u003Cli\u003EDevice trust is reset\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch4 id=\u0022global-logout\u0022\u003EGlobal Logout\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003EAll chains are revoked (optionally excluding current)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch4 id=\u0022root-revoke-1\u0022\u003ERoot Revoke\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003EEntire identity state is invalidated\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022event-dispatch\u0022\u003E5\uFE0F\u20E3 Event Dispatch\u003C/h3\u003E\n\u003Cp\u003EThe system emits:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogout events\u003C/li\u003E\n\u003Cli\u003EAudit logs\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022device-awareness\u0022\u003E\uD83D\uDCF1 Device Awareness\u003C/h2\u003E\n\u003Cp\u003ELogout behavior is device-aware:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEach device is a chain\u003C/li\u003E\n\u003Cli\u003ELogout can target specific devices\u003C/li\u003E\n\u003Cli\u003ESessions are grouped by device\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This enables fine-grained control\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022security-model\u0022\u003E\uD83D\uDD10 Security Model\u003C/h2\u003E\n\u003Ch3 id=\u0022controlled-termination\u0022\u003E\uD83D\uDD12 Controlled Termination\u003C/h3\u003E\n\u003Cp\u003EAll logout operations:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EPass through orchestrator\u003C/li\u003E\n\u003Cli\u003EAre validated by authority\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Prevents unauthorized session manipulation\u003C/p\u003E\n\u003Ch3 id=\u0022irreversible-revocation\u0022\u003E\uD83D\uDD01 Irreversible Revocation\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ERevoked chains cannot be restored\u003C/li\u003E\n\u003Cli\u003ERevoked sessions remain invalid\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Ensures strong security guarantees\u003C/p\u003E\n\u003Ch3 id=\u0022identity-boundaries\u0022\u003E\uD83D\uDD17 Identity Boundaries\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession \u2192 temporary identity proof\u003C/li\u003E\n\u003Cli\u003EChain \u2192 device trust boundary\u003C/li\u003E\n\u003Cli\u003ERoot \u2192 global identity state\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Logout operates within these boundaries\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Logout = ending a session\u003Cbr /\u003E\n\uD83D\uDC49 Revoke = resetting trust\u003C/p\u003E\n\u003Ch2 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogout and revoke are different operations\u003C/li\u003E\n\u003Cli\u003ELogout is reversible (via re-login)\u003C/li\u003E\n\u003Cli\u003ERevoke is permanent and forces new authentication\u003C/li\u003E\n\u003Cli\u003EDevice (chain) is a first-class concept\u003C/li\u003E\n\u003Cli\u003ESecurity is enforced through authority and orchestrator\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003ESession Lifecycle\u003C/strong\u003E\u003C/p\u003E\n" + "Html": "\n\u003Cp\u003EThe logout flow in UltimateAuth is not a single action.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 It represents different \u003Cstrong\u003Elevels of authentication termination\u003C/strong\u003E.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-is-logout\u0022\u003E\uD83E\uDDE0 What is Logout?\u003C/h2\u003E\n\u003Cp\u003EIn traditional systems:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogout = remove cookie or token\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EIn UltimateAuth:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Logout affects \u003Cstrong\u003Esession, device, or identity scope\u003C/strong\u003E\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003ELogout is not just removing access\u003Cbr /\u003E\n\u2192 It is controlling session lifecycle\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022logout-vs-revoke\u0022\u003E\uD83D\uDD00 Logout vs Revoke\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth distinguishes between two concepts:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022logout-soft-termination\u0022\u003E\uD83D\uDD39 Logout (Soft Termination)\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EEnds the current session\u003C/li\u003E\n\u003Cli\u003EKeeps the device (chain) active\u003C/li\u003E\n\u003Cli\u003EAllows re-login without resetting device context\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cpre\u003E\u003Ccode\u003ESession \u2192 Invalidated\nChain \u2192 Still Active\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 User can log in again and continue on the same device chain\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022revoke-hard-invalidation\u0022\u003E\uD83D\uDD25 Revoke (Hard Invalidation)\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EInvalidates session, chain, or root\u003C/li\u003E\n\u003Cli\u003ECannot be undone\u003C/li\u003E\n\u003Cli\u003EForces a completely new authentication path\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cpre\u003E\u003Ccode\u003EChain \u2192 Revoked\nSessions \u2192 Revoked\nNext login \u2192 New chain\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Revoke resets trust for that scope\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022levels-of-termination\u0022\u003E\uD83E\uDDE9 Levels of Termination\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth supports multiple logout scopes:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022session-level-logout\u0022\u003E\uD83D\uDD39 Session-Level Logout\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ETerminates a single session\u003C/li\u003E\n\u003Cli\u003EOther sessions on the same device may remain\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022device-level-chain\u0022\u003E\uD83D\uDCF1 Device-Level (Chain)\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ETerminates all sessions on a device\u003C/li\u003E\n\u003Cli\u003EDevice chain is invalidated or reset\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022global-logout-all-devices\u0022\u003E\uD83C\uDF10 Global Logout (All Devices)\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ETerminates all sessions across all devices\u003C/li\u003E\n\u003Cli\u003EKeeps root (user identity) intact\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022root-revoke\u0022\u003E\uD83D\uDD25 Root Revoke\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EInvalidates entire authentication state\u003C/li\u003E\n\u003Cli\u003EAll chains and sessions are revoked\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This is the strongest possible action\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022step-by-step-execution\u0022\u003E\uD83D\uDD04 Step-by-Step Execution\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022flow-context-resolution\u0022\u003E1\uFE0F\u20E3 Flow Context Resolution\u003C/h3\u003E\n\u003Cp\u003EThe system resolves:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECurrent session\u003C/li\u003E\n\u003Cli\u003EUser identity\u003C/li\u003E\n\u003Cli\u003ETenant\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022authority-decision\u0022\u003E2\uFE0F\u20E3 Authority Decision\u003C/h3\u003E\n\u003Cp\u003ELogout operations are validated:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAuthorization checks\u003C/li\u003E\n\u003Cli\u003EAccess validation\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Logout is not blindly executed\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022scope-determination\u0022\u003E3\uFE0F\u20E3 Scope Determination\u003C/h3\u003E\n\u003Cp\u003EThe system determines what to terminate:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession\u003C/li\u003E\n\u003Cli\u003EChain\u003C/li\u003E\n\u003Cli\u003ERoot\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022execution\u0022\u003E4\uFE0F\u20E3 Execution\u003C/h3\u003E\n\u003Cp\u003EDepending on scope:\u003C/p\u003E\n\u003Ch4 id=\u0022session-logout\u0022\u003ESession Logout\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession is revoked\u003C/li\u003E\n\u003Cli\u003EOther sessions unaffected\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch4 id=\u0022chain-revoke-logout\u0022\u003EChain Revoke / Logout\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003EAll sessions in the chain are revoked\u003C/li\u003E\n\u003Cli\u003EDevice trust is reset\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch4 id=\u0022global-logout\u0022\u003EGlobal Logout\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003EAll chains are revoked (optionally excluding current)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch4 id=\u0022root-revoke-1\u0022\u003ERoot Revoke\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003EEntire identity state is invalidated\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022event-dispatch\u0022\u003E5\uFE0F\u20E3 Event Dispatch\u003C/h3\u003E\n\u003Cp\u003EThe system emits:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogout events\u003C/li\u003E\n\u003Cli\u003EAudit logs\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022device-awareness\u0022\u003E\uD83D\uDCF1 Device Awareness\u003C/h2\u003E\n\u003Cp\u003ELogout behavior is device-aware:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEach device is a chain\u003C/li\u003E\n\u003Cli\u003ELogout can target specific devices\u003C/li\u003E\n\u003Cli\u003ESessions are grouped by device\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This enables fine-grained control\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-model\u0022\u003E\uD83D\uDD10 Security Model\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022controlled-termination\u0022\u003E\uD83D\uDD12 Controlled Termination\u003C/h3\u003E\n\u003Cp\u003EAll logout operations:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EPass through orchestrator\u003C/li\u003E\n\u003Cli\u003EAre validated by authority\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Prevents unauthorized session manipulation\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022irreversible-revocation\u0022\u003E\uD83D\uDD01 Irreversible Revocation\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ERevoked chains cannot be restored\u003C/li\u003E\n\u003Cli\u003ERevoked sessions remain invalid\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Ensures strong security guarantees\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022identity-boundaries\u0022\u003E\uD83D\uDD17 Identity Boundaries\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession \u2192 temporary identity proof\u003C/li\u003E\n\u003Cli\u003EChain \u2192 device trust boundary\u003C/li\u003E\n\u003Cli\u003ERoot \u2192 global identity state\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Logout operates within these boundaries\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Logout = ending a session\u003Cbr /\u003E\n\uD83D\uDC49 Revoke = resetting trust\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogout and revoke are different operations\u003C/li\u003E\n\u003Cli\u003ELogout is reversible (via re-login)\u003C/li\u003E\n\u003Cli\u003ERevoke is permanent and forces new authentication\u003C/li\u003E\n\u003Cli\u003EDevice (chain) is a first-class concept\u003C/li\u003E\n\u003Cli\u003ESecurity is enforced through authority and orchestrator\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003ESession Lifecycle\u003C/strong\u003E\u003C/p\u003E\n", + "Headings": [ + { + "Id": "what-is-logout", + "Text": "\uD83E\uDDE0 What is Logout?", + "Level": 0 + }, + { + "Id": "logout-vs-revoke", + "Text": "\uD83D\uDD00 Logout vs Revoke", + "Level": 0 + }, + { + "Id": "logout-soft-termination", + "Text": "\uD83D\uDD39 Logout (Soft Termination)", + "Level": 1 + }, + { + "Id": "revoke-hard-invalidation", + "Text": "\uD83D\uDD25 Revoke (Hard Invalidation)", + "Level": 1 + }, + { + "Id": "levels-of-termination", + "Text": "\uD83E\uDDE9 Levels of Termination", + "Level": 0 + }, + { + "Id": "session-level-logout", + "Text": "\uD83D\uDD39 Session-Level Logout", + "Level": 1 + }, + { + "Id": "device-level-chain", + "Text": "\uD83D\uDCF1 Device-Level (Chain)", + "Level": 1 + }, + { + "Id": "global-logout-all-devices", + "Text": "\uD83C\uDF10 Global Logout (All Devices)", + "Level": 1 + }, + { + "Id": "root-revoke", + "Text": "\uD83D\uDD25 Root Revoke", + "Level": 1 + }, + { + "Id": "step-by-step-execution", + "Text": "\uD83D\uDD04 Step-by-Step Execution", + "Level": 0 + }, + { + "Id": "flow-context-resolution", + "Text": "1\uFE0F\u20E3 Flow Context Resolution", + "Level": 1 + }, + { + "Id": "authority-decision", + "Text": "2\uFE0F\u20E3 Authority Decision", + "Level": 1 + }, + { + "Id": "scope-determination", + "Text": "3\uFE0F\u20E3 Scope Determination", + "Level": 1 + }, + { + "Id": "execution", + "Text": "4\uFE0F\u20E3 Execution", + "Level": 1 + }, + { + "Id": "event-dispatch", + "Text": "5\uFE0F\u20E3 Event Dispatch", + "Level": 1 + }, + { + "Id": "device-awareness", + "Text": "\uD83D\uDCF1 Device Awareness", + "Level": 0 + }, + { + "Id": "security-model", + "Text": "\uD83D\uDD10 Security Model", + "Level": 0 + }, + { + "Id": "controlled-termination", + "Text": "\uD83D\uDD12 Controlled Termination", + "Level": 1 + }, + { + "Id": "irreversible-revocation", + "Text": "\uD83D\uDD01 Irreversible Revocation", + "Level": 1 + }, + { + "Id": "identity-boundaries", + "Text": "\uD83D\uDD17 Identity Boundaries", + "Level": 1 + }, + { + "Id": "mental-model", + "Text": "\uD83E\uDDE0 Mental Model", + "Level": 0 + }, + { + "Id": "key-takeaways", + "Text": "\uD83D\uDCCC Key Takeaways", + "Level": 0 + }, + { + "Id": "next-step", + "Text": "\u27A1\uFE0F Next Step", + "Level": 0 + } + ] } \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/refresh-flow.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/refresh-flow.json index b204325f..f0c9d34e 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/refresh-flow.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/refresh-flow.json @@ -1,5 +1,107 @@ { "Slug": "auth-flows/refresh-flow", "Title": "Refresh", - "Html": "\n\u003Cp\u003EThe refresh flow in UltimateAuth is not a single fixed operation.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 It is a \u003Cstrong\u003Emode-dependent continuation strategy\u003C/strong\u003E.\u003C/p\u003E\n\u003Ch2 id=\u0022what-is-refresh\u0022\u003E\uD83E\uDDE0 What is Refresh?\u003C/h2\u003E\n\u003Cp\u003EIn traditional systems:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERefresh = get a new access token\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EIn UltimateAuth:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Refresh continues an existing authentication state\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003ERefresh is not re-authentication\u003Cbr /\u003E\n\u2192 It is session continuation\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Ch2 id=\u0022two-refresh-strategies\u0022\u003E\uD83D\uDD00 Two Refresh Strategies\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth supports two fundamentally different refresh behaviors:\u003C/p\u003E\n\u003Ch3 id=\u0022session-touch-stateful\u0022\u003E\uD83D\uDD39 Session Touch (Stateful)\u003C/h3\u003E\n\u003Cp\u003EUsed in \u003Cstrong\u003EPureOpaque mode\u003C/strong\u003E\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ENo tokens involved\u003C/li\u003E\n\u003Cli\u003ENo new session created\u003C/li\u003E\n\u003Cli\u003EExisting session is extended\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cpre\u003E\u003Ccode\u003ESession \u2192 Validate \u2192 Touch \u2192 Continue\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This updates activity without changing identity\u003C/p\u003E\n\u003Ch3 id=\u0022token-rotation-stateless-hybrid\u0022\u003E\uD83D\uDD39 Token Rotation (Stateless / Hybrid)\u003C/h3\u003E\n\u003Cp\u003EUsed in:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Cp\u003EHybrid\u003C/p\u003E\n\u003C/li\u003E\n\u003Cli\u003E\u003Cp\u003ESemiHybrid\u003C/p\u003E\n\u003C/li\u003E\n\u003Cli\u003E\u003Cp\u003EPureJwt\u003C/p\u003E\n\u003C/li\u003E\n\u003Cli\u003E\u003Cp\u003ERefresh token is validated\u003C/p\u003E\n\u003C/li\u003E\n\u003Cli\u003E\u003Cp\u003EOld token is revoked\u003C/p\u003E\n\u003C/li\u003E\n\u003Cli\u003E\u003Cp\u003ENew tokens are issued\u003C/p\u003E\n\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cpre\u003E\u003Ccode\u003ERefreshToken \u2192 Validate \u2192 Revoke \u2192 Issue New Tokens\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This ensures forward security\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mode-based-behavior\u0022\u003E\u2696\uFE0F Mode-Based Behavior\u003C/h2\u003E\n\u003Cp\u003ERefresh behavior is determined by the authentication mode:\u003C/p\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EMode\u003C/th\u003E\n\u003Cth\u003EBehavior\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EPureOpaque\u003C/td\u003E\n\u003Ctd\u003ESession Touch\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EHybrid\u003C/td\u003E\n\u003Ctd\u003ERotation \u002B Touch\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ESemiHybrid\u003C/td\u003E\n\u003Ctd\u003ERotation\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EPureJwt\u003C/td\u003E\n\u003Ctd\u003ERotation\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Cp\u003E\uD83D\uDC49 UltimateAuth automatically selects the correct strategy.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022step-by-step-execution\u0022\u003E\uD83D\uDD04 Step-by-Step Execution\u003C/h2\u003E\n\u003Ch3 id=\u0022input-resolution\u0022\u003E1\uFE0F\u20E3 Input Resolution\u003C/h3\u003E\n\u003Cp\u003EThe system resolves:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESessionId (if present)\u003C/li\u003E\n\u003Cli\u003ERefreshToken (if present)\u003C/li\u003E\n\u003Cli\u003EDevice context\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022mode-based-branching\u0022\u003E2\uFE0F\u20E3 Mode-Based Branching\u003C/h3\u003E\n\u003Cp\u003EThe system determines the refresh strategy:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession-based \u2192 Touch\u003C/li\u003E\n\u003Cli\u003EToken-based \u2192 Rotation\u003C/li\u003E\n\u003Cli\u003EHybrid \u2192 Both\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022session-validation\u0022\u003E3\uFE0F\u20E3 Session Validation\u003C/h3\u003E\n\u003Cp\u003EIf session is involved:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession is validated\u003C/li\u003E\n\u003Cli\u003EDevice binding is checked\u003C/li\u003E\n\u003Cli\u003EExpiration is evaluated\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022token-validation-if-applicable\u0022\u003E4\uFE0F\u20E3 Token Validation (if applicable)\u003C/h3\u003E\n\u003Cp\u003EIf refresh token is used:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EToken is validated\u003C/li\u003E\n\u003Cli\u003ESession and chain are verified\u003C/li\u003E\n\u003Cli\u003EDevice consistency is checked\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022security-checks\u0022\u003E5\uFE0F\u20E3 Security Checks\u003C/h3\u003E\n\u003Cp\u003EThe system enforces:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EToken reuse detection\u003C/li\u003E\n\u003Cli\u003ESession validity\u003C/li\u003E\n\u003Cli\u003EChain integrity\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 If validation fails \u2192 reauthentication required\u003C/p\u003E\n\u003Ch3 id=\u0022execution\u0022\u003E6\uFE0F\u20E3 Execution\u003C/h3\u003E\n\u003Cp\u003EDepending on the strategy:\u003C/p\u003E\n\u003Ch4 id=\u0022session-touch\u0022\u003ESession Touch\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003EUpdates \u003Ccode\u003ELastSeenAt\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003EApplies sliding expiration\u003C/li\u003E\n\u003Cli\u003ENo new tokens issued\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch4 id=\u0022token-rotation\u0022\u003EToken Rotation\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003ERevokes old refresh token\u003C/li\u003E\n\u003Cli\u003EIssues new access token\u003C/li\u003E\n\u003Cli\u003EIssues new refresh token\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch4 id=\u0022hybrid-mode\u0022\u003EHybrid Mode\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003EValidates session\u003C/li\u003E\n\u003Cli\u003ERotates tokens\u003C/li\u003E\n\u003Cli\u003EUpdates session activity\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022response-generation\u0022\u003E7\uFE0F\u20E3 Response Generation\u003C/h3\u003E\n\u003Cp\u003EThe response may include:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESessionId\u003C/li\u003E\n\u003Cli\u003EAccess token\u003C/li\u003E\n\u003Cli\u003ERefresh token\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Output depends on mode and client\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022security-model\u0022\u003E\uD83D\uDD10 Security Model\u003C/h2\u003E\n\u003Cp\u003EThe refresh flow includes strong protections:\u003C/p\u003E\n\u003Ch3 id=\u0022token-reuse-detection\u0022\u003E\uD83D\uDD01 Token Reuse Detection\u003C/h3\u003E\n\u003Cp\u003EIf a refresh token is reused:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EChain may be revoked\u003C/li\u003E\n\u003Cli\u003ESession may be revoked\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This prevents replay attacks\u003C/p\u003E\n\u003Ch3 id=\u0022session-binding\u0022\u003E\uD83D\uDD17 Session Binding\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ETokens are bound to session\u003C/li\u003E\n\u003Cli\u003ESession is bound to device\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Prevents token misuse across devices\u003C/p\u003E\n\u003Ch3 id=\u0022chain-integrity\u0022\u003E\uD83E\uDDEC Chain Integrity\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ERefresh operates within a chain\u003C/li\u003E\n\u003Cli\u003ECross-device usage is rejected\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Refresh = continuation\u003Cbr /\u003E\n\uD83D\uDC49 Not new authentication\u003C/p\u003E\n\u003Ch2 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003ERefresh behavior depends on auth mode\u003C/li\u003E\n\u003Cli\u003EStateful systems use session touch\u003C/li\u003E\n\u003Cli\u003EStateless systems use token rotation\u003C/li\u003E\n\u003Cli\u003EHybrid systems combine both\u003C/li\u003E\n\u003Cli\u003ESecurity is enforced at every step\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003ELogout Flow\u003C/strong\u003E\u003C/p\u003E\n" + "Html": "\n\u003Cp\u003EThe refresh flow in UltimateAuth is not a single fixed operation.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 It is a \u003Cstrong\u003Emode-dependent continuation strategy\u003C/strong\u003E.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-is-refresh\u0022\u003E\uD83E\uDDE0 What is Refresh?\u003C/h2\u003E\n\u003Cp\u003EIn traditional systems:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERefresh = get a new access token\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EIn UltimateAuth:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Refresh continues an existing authentication state\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003ERefresh is not re-authentication\u003Cbr /\u003E\n\u2192 It is session continuation\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022two-refresh-strategies\u0022\u003E\uD83D\uDD00 Two Refresh Strategies\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth supports two fundamentally different refresh behaviors:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022session-touch-stateful\u0022\u003E\uD83D\uDD39 Session Touch (Stateful)\u003C/h3\u003E\n\u003Cp\u003EUsed in \u003Cstrong\u003EPureOpaque mode\u003C/strong\u003E\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ENo tokens involved\u003C/li\u003E\n\u003Cli\u003ENo new session created\u003C/li\u003E\n\u003Cli\u003EExisting session is extended\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cpre\u003E\u003Ccode\u003ESession \u2192 Validate \u2192 Touch \u2192 Continue\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This updates activity without changing identity\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022token-rotation-stateless-hybrid\u0022\u003E\uD83D\uDD39 Token Rotation (Stateless / Hybrid)\u003C/h3\u003E\n\u003Cp\u003EUsed in:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Cp\u003EHybrid\u003C/p\u003E\n\u003C/li\u003E\n\u003Cli\u003E\u003Cp\u003ESemiHybrid\u003C/p\u003E\n\u003C/li\u003E\n\u003Cli\u003E\u003Cp\u003EPureJwt\u003C/p\u003E\n\u003C/li\u003E\n\u003Cli\u003E\u003Cp\u003ERefresh token is validated\u003C/p\u003E\n\u003C/li\u003E\n\u003Cli\u003E\u003Cp\u003EOld token is revoked\u003C/p\u003E\n\u003C/li\u003E\n\u003Cli\u003E\u003Cp\u003ENew tokens are issued\u003C/p\u003E\n\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cpre\u003E\u003Ccode\u003ERefreshToken \u2192 Validate \u2192 Revoke \u2192 Issue New Tokens\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This ensures forward security\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mode-based-behavior\u0022\u003E\u2696\uFE0F Mode-Based Behavior\u003C/h2\u003E\n\u003Cp\u003ERefresh behavior is determined by the authentication mode:\u003C/p\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EMode\u003C/th\u003E\n\u003Cth\u003EBehavior\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EPureOpaque\u003C/td\u003E\n\u003Ctd\u003ESession Touch\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EHybrid\u003C/td\u003E\n\u003Ctd\u003ERotation \u002B Touch\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ESemiHybrid\u003C/td\u003E\n\u003Ctd\u003ERotation\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EPureJwt\u003C/td\u003E\n\u003Ctd\u003ERotation\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Cp\u003E\uD83D\uDC49 UltimateAuth automatically selects the correct strategy.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022step-by-step-execution\u0022\u003E\uD83D\uDD04 Step-by-Step Execution\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022input-resolution\u0022\u003E1\uFE0F\u20E3 Input Resolution\u003C/h3\u003E\n\u003Cp\u003EThe system resolves:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESessionId (if present)\u003C/li\u003E\n\u003Cli\u003ERefreshToken (if present)\u003C/li\u003E\n\u003Cli\u003EDevice context\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022mode-based-branching\u0022\u003E2\uFE0F\u20E3 Mode-Based Branching\u003C/h3\u003E\n\u003Cp\u003EThe system determines the refresh strategy:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession-based \u2192 Touch\u003C/li\u003E\n\u003Cli\u003EToken-based \u2192 Rotation\u003C/li\u003E\n\u003Cli\u003EHybrid \u2192 Both\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022session-validation\u0022\u003E3\uFE0F\u20E3 Session Validation\u003C/h3\u003E\n\u003Cp\u003EIf session is involved:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession is validated\u003C/li\u003E\n\u003Cli\u003EDevice binding is checked\u003C/li\u003E\n\u003Cli\u003EExpiration is evaluated\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022token-validation-if-applicable\u0022\u003E4\uFE0F\u20E3 Token Validation (if applicable)\u003C/h3\u003E\n\u003Cp\u003EIf refresh token is used:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EToken is validated\u003C/li\u003E\n\u003Cli\u003ESession and chain are verified\u003C/li\u003E\n\u003Cli\u003EDevice consistency is checked\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-checks\u0022\u003E5\uFE0F\u20E3 Security Checks\u003C/h3\u003E\n\u003Cp\u003EThe system enforces:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EToken reuse detection\u003C/li\u003E\n\u003Cli\u003ESession validity\u003C/li\u003E\n\u003Cli\u003EChain integrity\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 If validation fails \u2192 reauthentication required\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022execution\u0022\u003E6\uFE0F\u20E3 Execution\u003C/h3\u003E\n\u003Cp\u003EDepending on the strategy:\u003C/p\u003E\n\u003Ch4 id=\u0022session-touch\u0022\u003ESession Touch\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003EUpdates \u003Ccode\u003ELastSeenAt\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003EApplies sliding expiration\u003C/li\u003E\n\u003Cli\u003ENo new tokens issued\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch4 id=\u0022token-rotation\u0022\u003EToken Rotation\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003ERevokes old refresh token\u003C/li\u003E\n\u003Cli\u003EIssues new access token\u003C/li\u003E\n\u003Cli\u003EIssues new refresh token\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch4 id=\u0022hybrid-mode\u0022\u003EHybrid Mode\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003EValidates session\u003C/li\u003E\n\u003Cli\u003ERotates tokens\u003C/li\u003E\n\u003Cli\u003EUpdates session activity\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022response-generation\u0022\u003E7\uFE0F\u20E3 Response Generation\u003C/h3\u003E\n\u003Cp\u003EThe response may include:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESessionId\u003C/li\u003E\n\u003Cli\u003EAccess token\u003C/li\u003E\n\u003Cli\u003ERefresh token\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Output depends on mode and client\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-model\u0022\u003E\uD83D\uDD10 Security Model\u003C/h2\u003E\n\u003Cp\u003EThe refresh flow includes strong protections:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022token-reuse-detection\u0022\u003E\uD83D\uDD01 Token Reuse Detection\u003C/h3\u003E\n\u003Cp\u003EIf a refresh token is reused:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EChain may be revoked\u003C/li\u003E\n\u003Cli\u003ESession may be revoked\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This prevents replay attacks\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022session-binding\u0022\u003E\uD83D\uDD17 Session Binding\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ETokens are bound to session\u003C/li\u003E\n\u003Cli\u003ESession is bound to device\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Prevents token misuse across devices\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022chain-integrity\u0022\u003E\uD83E\uDDEC Chain Integrity\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ERefresh operates within a chain\u003C/li\u003E\n\u003Cli\u003ECross-device usage is rejected\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Refresh = continuation\u003Cbr /\u003E\n\uD83D\uDC49 Not new authentication\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003ERefresh behavior depends on auth mode\u003C/li\u003E\n\u003Cli\u003EStateful systems use session touch\u003C/li\u003E\n\u003Cli\u003EStateless systems use token rotation\u003C/li\u003E\n\u003Cli\u003EHybrid systems combine both\u003C/li\u003E\n\u003Cli\u003ESecurity is enforced at every step\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003ELogout Flow\u003C/strong\u003E\u003C/p\u003E\n", + "Headings": [ + { + "Id": "what-is-refresh", + "Text": "\uD83E\uDDE0 What is Refresh?", + "Level": 0 + }, + { + "Id": "two-refresh-strategies", + "Text": "\uD83D\uDD00 Two Refresh Strategies", + "Level": 0 + }, + { + "Id": "session-touch-stateful", + "Text": "\uD83D\uDD39 Session Touch (Stateful)", + "Level": 1 + }, + { + "Id": "token-rotation-stateless-hybrid", + "Text": "\uD83D\uDD39 Token Rotation (Stateless / Hybrid)", + "Level": 1 + }, + { + "Id": "mode-based-behavior", + "Text": "\u2696\uFE0F Mode-Based Behavior", + "Level": 0 + }, + { + "Id": "step-by-step-execution", + "Text": "\uD83D\uDD04 Step-by-Step Execution", + "Level": 0 + }, + { + "Id": "input-resolution", + "Text": "1\uFE0F\u20E3 Input Resolution", + "Level": 1 + }, + { + "Id": "mode-based-branching", + "Text": "2\uFE0F\u20E3 Mode-Based Branching", + "Level": 1 + }, + { + "Id": "session-validation", + "Text": "3\uFE0F\u20E3 Session Validation", + "Level": 1 + }, + { + "Id": "token-validation-if-applicable", + "Text": "4\uFE0F\u20E3 Token Validation (if applicable)", + "Level": 1 + }, + { + "Id": "security-checks", + "Text": "5\uFE0F\u20E3 Security Checks", + "Level": 1 + }, + { + "Id": "execution", + "Text": "6\uFE0F\u20E3 Execution", + "Level": 1 + }, + { + "Id": "response-generation", + "Text": "7\uFE0F\u20E3 Response Generation", + "Level": 1 + }, + { + "Id": "security-model", + "Text": "\uD83D\uDD10 Security Model", + "Level": 0 + }, + { + "Id": "token-reuse-detection", + "Text": "\uD83D\uDD01 Token Reuse Detection", + "Level": 1 + }, + { + "Id": "session-binding", + "Text": "\uD83D\uDD17 Session Binding", + "Level": 1 + }, + { + "Id": "chain-integrity", + "Text": "\uD83E\uDDEC Chain Integrity", + "Level": 1 + }, + { + "Id": "mental-model", + "Text": "\uD83E\uDDE0 Mental Model", + "Level": 0 + }, + { + "Id": "key-takeaways", + "Text": "\uD83D\uDCCC Key Takeaways", + "Level": 0 + }, + { + "Id": "next-step", + "Text": "\u27A1\uFE0F Next Step", + "Level": 0 + } + ] } \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/session-lifecycle.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/session-lifecycle.json index 909bb101..96fc0cfb 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/session-lifecycle.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/session-lifecycle.json @@ -1,5 +1,102 @@ { "Slug": "auth-flows/session-lifecycle", "Title": "Session Lifecycle", - "Html": "\n\u003Cp\u003EUltimateAuth is built around a structured session model.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Authentication is not a token\u003Cbr /\u003E\n\uD83D\uDC49 It is a \u003Cstrong\u003Ehierarchical session system\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022core-model\u0022\u003E\uD83E\uDDE0 Core Model\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth defines three core entities:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003ERoot \u2192 Chain \u2192 Session\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022root-identity-authority\u0022\u003E\uD83D\uDD39 Root (Identity Authority)\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EOne per user\u003C/li\u003E\n\u003Cli\u003ERepresents global authentication state\u003C/li\u003E\n\u003Cli\u003EHolds security version\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Root defines \u003Cstrong\u003Ewho the user is\u003C/strong\u003E\u003C/p\u003E\n\u003Ch3 id=\u0022chain-device-context\u0022\u003E\uD83D\uDCF1 Chain (Device Context)\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EOne per device\u003C/li\u003E\n\u003Cli\u003ERepresents a device-bound identity context\u003C/li\u003E\n\u003Cli\u003ETracks activity (LastSeenAt)\u003C/li\u003E\n\u003Cli\u003EManages session rotation and touch\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 A chain is a \u003Cstrong\u003Etrusted device boundary\u003C/strong\u003E\u003C/p\u003E\n\u003Ch3 id=\u0022session-authentication-instance\u0022\u003E\uD83D\uDD11 Session (Authentication Instance)\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ECreated on login\u003C/li\u003E\n\u003Cli\u003ERepresents a single authentication event\u003C/li\u003E\n\u003Cli\u003EHas expiration and revocation state\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 A session is a \u003Cstrong\u003Eproof of authentication\u003C/strong\u003E\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022relationship\u0022\u003E\uD83D\uDD17 Relationship\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode\u003EUser\n\u2514\u2500\u2500 Root\n\u2514\u2500\u2500 Chain (Device)\n\u2514\u2500\u2500 Session (Instance)\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Each level adds more specificity:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERoot \u2192 identity\u003C/li\u003E\n\u003Cli\u003EChain \u2192 device\u003C/li\u003E\n\u003Cli\u003ESession \u2192 login instance\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022lifecycle-overview\u0022\u003E\uD83D\uDD04 Lifecycle Overview\u003C/h2\u003E\n\u003Ch3 id=\u0022creation-login\u0022\u003E1\uFE0F\u20E3 Creation (Login)\u003C/h3\u003E\n\u003Cp\u003EWhen a user logs in:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERoot is created (if not exists)\u003C/li\u003E\n\u003Cli\u003EChain is resolved or created\u003C/li\u003E\n\u003Cli\u003ESession is issued\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022active-usage\u0022\u003E2\uFE0F\u20E3 Active Usage\u003C/h3\u003E\n\u003Cp\u003EDuring normal operation:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession is validated\u003C/li\u003E\n\u003Cli\u003EChain \u003Ccode\u003ELastSeenAt\u003C/code\u003E is updated (touch)\u003C/li\u003E\n\u003Cli\u003ESliding expiration may apply\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Activity updates the \u003Cstrong\u003Echain\u003C/strong\u003E, not just the session\u003C/p\u003E\n\u003Ch3 id=\u0022refresh\u0022\u003E3\uFE0F\u20E3 Refresh\u003C/h3\u003E\n\u003Cp\u003EDepending on mode:\u003C/p\u003E\n\u003Ch4 id=\u0022session-based-pureopaque\u0022\u003ESession-Based (PureOpaque)\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession remains\u003C/li\u003E\n\u003Cli\u003EChain is touched\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch4 id=\u0022token-based-hybrid-jwt\u0022\u003EToken-Based (Hybrid / JWT)\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession continues\u003C/li\u003E\n\u003Cli\u003ETokens are rotated\u003C/li\u003E\n\u003Cli\u003EChain rotation count increases\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Chain tracks behavior:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERotationCount\u003C/li\u003E\n\u003Cli\u003ETouchCount\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022expiration\u0022\u003E4\uFE0F\u20E3 Expiration\u003C/h3\u003E\n\u003Cp\u003EA session may expire due to:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELifetime expiration\u003C/li\u003E\n\u003Cli\u003EIdle timeout\u003C/li\u003E\n\u003Cli\u003EAbsolute expiration\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Expired \u2260 revoked\u003C/p\u003E\n\u003Ch3 id=\u0022revocation\u0022\u003E5\uFE0F\u20E3 Revocation\u003C/h3\u003E\n\u003Cp\u003ERevocation can occur at multiple levels:\u003C/p\u003E\n\u003Ch4 id=\u0022session-revocation\u0022\u003ESession Revocation\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003ESingle session invalidated\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch4 id=\u0022chain-revocation\u0022\u003EChain Revocation\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003EAll sessions on device invalidated\u003C/li\u003E\n\u003Cli\u003EDevice trust reset\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Ch4 id=\u0022root-revocation\u0022\u003ERoot Revocation\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003EAll chains and sessions invalidated\u003C/li\u003E\n\u003Cli\u003ESecurity version increased\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Revocation is irreversible\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022security-model\u0022\u003E\uD83D\uDD10 Security Model\u003C/h2\u003E\n\u003Ch3 id=\u0022security-versioning\u0022\u003E\uD83D\uDD12 Security Versioning\u003C/h3\u003E\n\u003Cp\u003EEach root has:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003ESecurityVersion\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EEach session stores:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003ESecurityVersionAtCreation\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Cp\u003E\uD83D\uDC49 If mismatch:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ESession becomes invalid\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022device-binding\u0022\u003E\uD83D\uDD17 Device Binding\u003C/h3\u003E\n\u003Cp\u003EEach chain is tied to:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EDeviceId\u003C/li\u003E\n\u003Cli\u003EPlatform\u003C/li\u003E\n\u003Cli\u003EOS\u003C/li\u003E\n\u003Cli\u003EBrowser\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Prevents cross-device misuse\u003C/p\u003E\n\u003Ch3 id=\u0022rotation-tracking\u0022\u003E\uD83D\uDD01 Rotation Tracking\u003C/h3\u003E\n\u003Cp\u003EChains track:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERotationCount\u003C/li\u003E\n\u003Cli\u003ETouchCount\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Enables:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Ereplay detection\u003C/li\u003E\n\u003Cli\u003Eanomaly tracking\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022lifecycle-configuration\u0022\u003E\u2699\uFE0F Lifecycle Configuration\u003C/h3\u003E\n\u003Cp\u003ESession behavior is configurable:\u003C/p\u003E\n\u003Cp\u003E\u23F1 Lifetime\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EDefault session duration\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDD04 Sliding Expiration\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EExtends session on activity\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDCA4 Idle Timeout\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EInvalidates inactive sessions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDCF1 Device Limits\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EMax chains per user\u003C/li\u003E\n\u003Cli\u003EMax sessions per chain\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 These are defined via UAuthSessionOptions\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Authentication is a living structure\n\uD83D\uDC49 Not a static token\u003C/p\u003E\n\u003Ch2 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003ESessions are part of a hierarchy\u003C/li\u003E\n\u003Cli\u003EDevice (chain) is a first-class concept\u003C/li\u003E\n\u003Cli\u003ERoot controls global security\u003C/li\u003E\n\u003Cli\u003ESessions are short-lived proofs\u003C/li\u003E\n\u003Cli\u003EChains manage lifecycle and activity\u003C/li\u003E\n\u003Cli\u003ERevocation operates at multiple levels\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to Token Behavior\u003C/p\u003E\n" + "Html": "\n\u003Cp\u003EUltimateAuth is built around a structured session model.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Authentication is not a token\u003Cbr /\u003E\n\uD83D\uDC49 It is a \u003Cstrong\u003Ehierarchical session system\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022core-model\u0022\u003E\uD83E\uDDE0 Core Model\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth defines three core entities:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003ERoot \u2192 Chain \u2192 Session\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022root-identity-authority\u0022\u003E\uD83D\uDD39 Root (Identity Authority)\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EOne per user\u003C/li\u003E\n\u003Cli\u003ERepresents global authentication state\u003C/li\u003E\n\u003Cli\u003EHolds security version\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Root defines \u003Cstrong\u003Ewho the user is\u003C/strong\u003E\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022chain-device-context\u0022\u003E\uD83D\uDCF1 Chain (Device Context)\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EOne per device\u003C/li\u003E\n\u003Cli\u003ERepresents a device-bound identity context\u003C/li\u003E\n\u003Cli\u003ETracks activity (LastSeenAt)\u003C/li\u003E\n\u003Cli\u003EManages session rotation and touch\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 A chain is a \u003Cstrong\u003Etrusted device boundary\u003C/strong\u003E\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022session-authentication-instance\u0022\u003E\uD83D\uDD11 Session (Authentication Instance)\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ECreated on login\u003C/li\u003E\n\u003Cli\u003ERepresents a single authentication event\u003C/li\u003E\n\u003Cli\u003EHas expiration and revocation state\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 A session is a \u003Cstrong\u003Eproof of authentication\u003C/strong\u003E\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022relationship\u0022\u003E\uD83D\uDD17 Relationship\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode\u003EUser\n\u2514\u2500\u2500 Root\n\u2514\u2500\u2500 Chain (Device)\n\u2514\u2500\u2500 Session (Instance)\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Each level adds more specificity:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERoot \u2192 identity\u003C/li\u003E\n\u003Cli\u003EChain \u2192 device\u003C/li\u003E\n\u003Cli\u003ESession \u2192 login instance\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022lifecycle-overview\u0022\u003E\uD83D\uDD04 Lifecycle Overview\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022creation-login\u0022\u003E1\uFE0F\u20E3 Creation (Login)\u003C/h3\u003E\n\u003Cp\u003EWhen a user logs in:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERoot is created (if not exists)\u003C/li\u003E\n\u003Cli\u003EChain is resolved or created\u003C/li\u003E\n\u003Cli\u003ESession is issued\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022active-usage\u0022\u003E2\uFE0F\u20E3 Active Usage\u003C/h3\u003E\n\u003Cp\u003EDuring normal operation:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession is validated\u003C/li\u003E\n\u003Cli\u003EChain \u003Ccode\u003ELastSeenAt\u003C/code\u003E is updated (touch)\u003C/li\u003E\n\u003Cli\u003ESliding expiration may apply\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Activity updates the \u003Cstrong\u003Echain\u003C/strong\u003E, not just the session\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022refresh\u0022\u003E3\uFE0F\u20E3 Refresh\u003C/h3\u003E\n\u003Cp\u003EDepending on mode:\u003C/p\u003E\n\u003Ch4 id=\u0022session-based-pureopaque\u0022\u003ESession-Based (PureOpaque)\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession remains\u003C/li\u003E\n\u003Cli\u003EChain is touched\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch4 id=\u0022token-based-hybrid-jwt\u0022\u003EToken-Based (Hybrid / JWT)\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession continues\u003C/li\u003E\n\u003Cli\u003ETokens are rotated\u003C/li\u003E\n\u003Cli\u003EChain rotation count increases\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Chain tracks behavior:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERotationCount\u003C/li\u003E\n\u003Cli\u003ETouchCount\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022expiration\u0022\u003E4\uFE0F\u20E3 Expiration\u003C/h3\u003E\n\u003Cp\u003EA session may expire due to:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELifetime expiration\u003C/li\u003E\n\u003Cli\u003EIdle timeout\u003C/li\u003E\n\u003Cli\u003EAbsolute expiration\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Expired \u2260 revoked\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022revocation\u0022\u003E5\uFE0F\u20E3 Revocation\u003C/h3\u003E\n\u003Cp\u003ERevocation can occur at multiple levels:\u003C/p\u003E\n\u003Ch4 id=\u0022session-revocation\u0022\u003ESession Revocation\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003ESingle session invalidated\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch4 id=\u0022chain-revocation\u0022\u003EChain Revocation\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003EAll sessions on device invalidated\u003C/li\u003E\n\u003Cli\u003EDevice trust reset\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Ch4 id=\u0022root-revocation\u0022\u003ERoot Revocation\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003EAll chains and sessions invalidated\u003C/li\u003E\n\u003Cli\u003ESecurity version increased\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Revocation is irreversible\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-model\u0022\u003E\uD83D\uDD10 Security Model\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-versioning\u0022\u003E\uD83D\uDD12 Security Versioning\u003C/h3\u003E\n\u003Cp\u003EEach root has:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003ESecurityVersion\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EEach session stores:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003ESecurityVersionAtCreation\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Cp\u003E\uD83D\uDC49 If mismatch:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ESession becomes invalid\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022device-binding\u0022\u003E\uD83D\uDD17 Device Binding\u003C/h3\u003E\n\u003Cp\u003EEach chain is tied to:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EDeviceId\u003C/li\u003E\n\u003Cli\u003EPlatform\u003C/li\u003E\n\u003Cli\u003EOS\u003C/li\u003E\n\u003Cli\u003EBrowser\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Prevents cross-device misuse\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022rotation-tracking\u0022\u003E\uD83D\uDD01 Rotation Tracking\u003C/h3\u003E\n\u003Cp\u003EChains track:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERotationCount\u003C/li\u003E\n\u003Cli\u003ETouchCount\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Enables:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Ereplay detection\u003C/li\u003E\n\u003Cli\u003Eanomaly tracking\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022lifecycle-configuration\u0022\u003E\u2699\uFE0F Lifecycle Configuration\u003C/h3\u003E\n\u003Cp\u003ESession behavior is configurable:\u003C/p\u003E\n\u003Cp\u003E\u23F1 Lifetime\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EDefault session duration\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDD04 Sliding Expiration\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EExtends session on activity\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDCA4 Idle Timeout\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EInvalidates inactive sessions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDCF1 Device Limits\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EMax chains per user\u003C/li\u003E\n\u003Cli\u003EMax sessions per chain\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 These are defined via UAuthSessionOptions\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Authentication is a living structure\n\uD83D\uDC49 Not a static token\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003ESessions are part of a hierarchy\u003C/li\u003E\n\u003Cli\u003EDevice (chain) is a first-class concept\u003C/li\u003E\n\u003Cli\u003ERoot controls global security\u003C/li\u003E\n\u003Cli\u003ESessions are short-lived proofs\u003C/li\u003E\n\u003Cli\u003EChains manage lifecycle and activity\u003C/li\u003E\n\u003Cli\u003ERevocation operates at multiple levels\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to Token Behavior\u003C/p\u003E\n", + "Headings": [ + { + "Id": "core-model", + "Text": "\uD83E\uDDE0 Core Model", + "Level": 0 + }, + { + "Id": "root-identity-authority", + "Text": "\uD83D\uDD39 Root (Identity Authority)", + "Level": 1 + }, + { + "Id": "chain-device-context", + "Text": "\uD83D\uDCF1 Chain (Device Context)", + "Level": 1 + }, + { + "Id": "session-authentication-instance", + "Text": "\uD83D\uDD11 Session (Authentication Instance)", + "Level": 1 + }, + { + "Id": "relationship", + "Text": "\uD83D\uDD17 Relationship", + "Level": 0 + }, + { + "Id": "lifecycle-overview", + "Text": "\uD83D\uDD04 Lifecycle Overview", + "Level": 0 + }, + { + "Id": "creation-login", + "Text": "1\uFE0F\u20E3 Creation (Login)", + "Level": 1 + }, + { + "Id": "active-usage", + "Text": "2\uFE0F\u20E3 Active Usage", + "Level": 1 + }, + { + "Id": "refresh", + "Text": "3\uFE0F\u20E3 Refresh", + "Level": 1 + }, + { + "Id": "expiration", + "Text": "4\uFE0F\u20E3 Expiration", + "Level": 1 + }, + { + "Id": "revocation", + "Text": "5\uFE0F\u20E3 Revocation", + "Level": 1 + }, + { + "Id": "security-model", + "Text": "\uD83D\uDD10 Security Model", + "Level": 0 + }, + { + "Id": "security-versioning", + "Text": "\uD83D\uDD12 Security Versioning", + "Level": 1 + }, + { + "Id": "device-binding", + "Text": "\uD83D\uDD17 Device Binding", + "Level": 1 + }, + { + "Id": "rotation-tracking", + "Text": "\uD83D\uDD01 Rotation Tracking", + "Level": 1 + }, + { + "Id": "lifecycle-configuration", + "Text": "\u2699\uFE0F Lifecycle Configuration", + "Level": 1 + }, + { + "Id": "mental-model", + "Text": "\uD83E\uDDE0 Mental Model", + "Level": 0 + }, + { + "Id": "key-takeaways", + "Text": "\uD83D\uDCCC Key Takeaways", + "Level": 0 + }, + { + "Id": "next-step", + "Text": "\u27A1\uFE0F Next Step", + "Level": 0 + } + ] } \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/token-behavior.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/token-behavior.json index 957f857b..cc2615db 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/token-behavior.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/auth-flows/token-behavior.json @@ -1,5 +1,117 @@ { "Slug": "auth-flows/token-behavior", "Title": "Token Behavior", - "Html": "\n\u003Cp\u003EIn UltimateAuth, tokens are not the foundation of authentication.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 They are \u003Cstrong\u003Ederived artifacts of a session\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022rethinking-tokens\u0022\u003E\uD83E\uDDE0 Rethinking Tokens\u003C/h2\u003E\n\u003Cp\u003EIn traditional systems:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EToken = identity\u003C/li\u003E\n\u003Cli\u003EToken = authentication\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EIn UltimateAuth:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Session = identity\u003Cbr /\u003E\n\uD83D\uDC49 Token = transport mechanism\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003ETokens do not define identity\u003Cbr /\u003E\n\u2192 Sessions do\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Ch2 id=\u0022token-types\u0022\u003E\uD83E\uDDE9 Token Types\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth supports two main token types:\u003C/p\u003E\n\u003Ch3 id=\u0022opaque-tokens\u0022\u003E\uD83D\uDD39 Opaque Tokens\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ERandom, non-decodable values\u003C/li\u003E\n\u003Cli\u003EStored and validated on the server\u003C/li\u003E\n\u003Cli\u003ETypically reference a session\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Used in:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EPureOpaque\u003C/li\u003E\n\u003Cli\u003EHybrid\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022jwt-json-web-tokens\u0022\u003E\uD83D\uDD39 JWT (JSON Web Tokens)\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ESelf-contained tokens\u003C/li\u003E\n\u003Cli\u003EInclude claims and metadata\u003C/li\u003E\n\u003Cli\u003ESigned and verifiable without server lookup\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Used in:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESemiHybrid\u003C/li\u003E\n\u003Cli\u003EPureJwt\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mode-based-behavior\u0022\u003E\u2696\uFE0F Mode-Based Behavior\u003C/h2\u003E\n\u003Cp\u003EToken behavior depends on the authentication mode:\u003C/p\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EMode\u003C/th\u003E\n\u003Cth\u003EAccess Token\u003C/th\u003E\n\u003Cth\u003ERefresh Token\u003C/th\u003E\n\u003Cth\u003EBehavior\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EPureOpaque\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003ESession-only\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EHybrid\u003C/td\u003E\n\u003Ctd\u003E\u2714 (opaque/JWT)\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003Ctd\u003ESession \u002B token\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ESemiHybrid\u003C/td\u003E\n\u003Ctd\u003E\u2714 (JWT)\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003Ctd\u003EJWT \u002B session metadata\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EPureJwt\u003C/td\u003E\n\u003Ctd\u003E\u2714 (JWT)\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003Ctd\u003EFully stateless\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Cp\u003E\uD83D\uDC49 UltimateAuth selects behavior automatically\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022access-tokens\u0022\u003E\uD83D\uDD11 Access Tokens\u003C/h2\u003E\n\u003Cp\u003EAccess tokens represent:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 A \u003Cstrong\u003Etemporary access grant\u003C/strong\u003E\u003C/p\u003E\n\u003Ch3 id=\u0022characteristics\u0022\u003ECharacteristics\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EShort-lived\u003C/li\u003E\n\u003Cli\u003EMode-dependent format\u003C/li\u003E\n\u003Cli\u003EMay contain session reference (\u003Ccode\u003Esid\u003C/code\u003E)\u003C/li\u003E\n\u003Cli\u003EMay include claims\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022important\u0022\u003EImportant\u003C/h3\u003E\n\u003Cp\u003EAccess token is NOT the source of truth.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 It reflects session state, not replaces it\u003C/p\u003E\n\u003Ch2 id=\u0022refresh-tokens\u0022\u003E\uD83D\uDD04 Refresh Tokens\u003C/h2\u003E\n\u003Cp\u003ERefresh tokens represent:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 A \u003Cstrong\u003Econtinuation mechanism\u003C/strong\u003E\u003C/p\u003E\n\u003Ch3 id=\u0022characteristics-1\u0022\u003ECharacteristics\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ELong-lived\u003C/li\u003E\n\u003Cli\u003EStored as hashed values\u003C/li\u003E\n\u003Cli\u003EBound to session and optionally chain\u003C/li\u003E\n\u003Cli\u003ERotated on use\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022lifecycle\u0022\u003ELifecycle\u003C/h3\u003E\n\u003Cp\u003EIssued \u2192 Used \u2192 Replaced \u2192 Revoked\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Old tokens are invalidated on rotation\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022security-model\u0022\u003E\uD83D\uDD10 Security Model\u003C/h2\u003E\n\u003Ch3 id=\u0022rotation\u0022\u003E\uD83D\uDD01 Rotation\u003C/h3\u003E\n\u003Cp\u003EEach refresh:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EInvalidates previous token\u003C/li\u003E\n\u003Cli\u003EIssues a new token\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Prevents replay attacks\u003C/p\u003E\n\u003Ch3 id=\u0022reuse-detection\u0022\u003E\uD83D\uDEA8 Reuse Detection\u003C/h3\u003E\n\u003Cp\u003EIf a token is reused:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EChain may be revoked\u003C/li\u003E\n\u003Cli\u003ESession may be revoked\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Strong forward security\u003C/p\u003E\n\u003Ch3 id=\u0022session-binding\u0022\u003E\uD83D\uDD17 Session Binding\u003C/h3\u003E\n\u003Cp\u003ERefresh tokens include:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESessionId\u003C/li\u003E\n\u003Cli\u003EChainId (optional)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Prevents cross-context usage\u003C/p\u003E\n\u003Ch3 id=\u0022hashed-storage\u0022\u003E\uD83D\uDD12 Hashed Storage\u003C/h3\u003E\n\u003Cp\u003ETokens are:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ENever stored as plaintext\u003C/li\u003E\n\u003Cli\u003EHashed using secure algorithms\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Reduces breach impact\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022token-issuance\u0022\u003E\uD83D\uDD04 Token Issuance\u003C/h2\u003E\n\u003Cp\u003ETokens are issued during:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogin\u003C/li\u003E\n\u003Cli\u003ERefresh\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022access-token\u0022\u003EAccess Token\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EMay be opaque or JWT\u003C/li\u003E\n\u003Cli\u003EIncludes identity and optional session reference\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022refresh-token\u0022\u003ERefresh Token\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EAlways opaque\u003C/li\u003E\n\u003Cli\u003EPersisted in secure store\u003C/li\u003E\n\u003Cli\u003EUsed only for rotation\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Tokens are not identity\u003Cbr /\u003E\n\uD83D\uDC49 They are projections of a session\u003C/p\u003E\n\u003Ch2 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession is the source of truth\u003C/li\u003E\n\u003Cli\u003ETokens are optional and mode-dependent\u003C/li\u003E\n\u003Cli\u003EOpaque tokens require server validation\u003C/li\u003E\n\u003Cli\u003EJWT tokens allow stateless access\u003C/li\u003E\n\u003Cli\u003ERefresh tokens enable secure continuation\u003C/li\u003E\n\u003Cli\u003EToken rotation ensures forward security\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to Device Management\u003C/p\u003E\n" + "Html": "\n\u003Cp\u003EIn UltimateAuth, tokens are not the foundation of authentication.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 They are \u003Cstrong\u003Ederived artifacts of a session\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022rethinking-tokens\u0022\u003E\uD83E\uDDE0 Rethinking Tokens\u003C/h2\u003E\n\u003Cp\u003EIn traditional systems:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EToken = identity\u003C/li\u003E\n\u003Cli\u003EToken = authentication\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EIn UltimateAuth:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Session = identity\u003Cbr /\u003E\n\uD83D\uDC49 Token = transport mechanism\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003ETokens do not define identity\u003Cbr /\u003E\n\u2192 Sessions do\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022token-types\u0022\u003E\uD83E\uDDE9 Token Types\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth supports two main token types:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022opaque-tokens\u0022\u003E\uD83D\uDD39 Opaque Tokens\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ERandom, non-decodable values\u003C/li\u003E\n\u003Cli\u003EStored and validated on the server\u003C/li\u003E\n\u003Cli\u003ETypically reference a session\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Used in:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EPureOpaque\u003C/li\u003E\n\u003Cli\u003EHybrid\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022jwt-json-web-tokens\u0022\u003E\uD83D\uDD39 JWT (JSON Web Tokens)\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ESelf-contained tokens\u003C/li\u003E\n\u003Cli\u003EInclude claims and metadata\u003C/li\u003E\n\u003Cli\u003ESigned and verifiable without server lookup\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Used in:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESemiHybrid\u003C/li\u003E\n\u003Cli\u003EPureJwt\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mode-based-behavior\u0022\u003E\u2696\uFE0F Mode-Based Behavior\u003C/h2\u003E\n\u003Cp\u003EToken behavior depends on the authentication mode:\u003C/p\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EMode\u003C/th\u003E\n\u003Cth\u003EAccess Token\u003C/th\u003E\n\u003Cth\u003ERefresh Token\u003C/th\u003E\n\u003Cth\u003EBehavior\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EPureOpaque\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003ESession-only\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EHybrid\u003C/td\u003E\n\u003Ctd\u003E\u2714 (opaque/JWT)\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003Ctd\u003ESession \u002B token\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ESemiHybrid\u003C/td\u003E\n\u003Ctd\u003E\u2714 (JWT)\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003Ctd\u003EJWT \u002B session metadata\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EPureJwt\u003C/td\u003E\n\u003Ctd\u003E\u2714 (JWT)\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003Ctd\u003EFully stateless\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Cp\u003E\uD83D\uDC49 UltimateAuth selects behavior automatically\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022access-tokens\u0022\u003E\uD83D\uDD11 Access Tokens\u003C/h2\u003E\n\u003Cp\u003EAccess tokens represent:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 A \u003Cstrong\u003Etemporary access grant\u003C/strong\u003E\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022characteristics\u0022\u003ECharacteristics\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EShort-lived\u003C/li\u003E\n\u003Cli\u003EMode-dependent format\u003C/li\u003E\n\u003Cli\u003EMay contain session reference (\u003Ccode\u003Esid\u003C/code\u003E)\u003C/li\u003E\n\u003Cli\u003EMay include claims\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022important\u0022\u003EImportant\u003C/h3\u003E\n\u003Cp\u003EAccess token is NOT the source of truth.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 It reflects session state, not replaces it\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022refresh-tokens\u0022\u003E\uD83D\uDD04 Refresh Tokens\u003C/h2\u003E\n\u003Cp\u003ERefresh tokens represent:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 A \u003Cstrong\u003Econtinuation mechanism\u003C/strong\u003E\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022characteristics-1\u0022\u003ECharacteristics\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ELong-lived\u003C/li\u003E\n\u003Cli\u003EStored as hashed values\u003C/li\u003E\n\u003Cli\u003EBound to session and optionally chain\u003C/li\u003E\n\u003Cli\u003ERotated on use\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022lifecycle\u0022\u003ELifecycle\u003C/h3\u003E\n\u003Cp\u003EIssued \u2192 Used \u2192 Replaced \u2192 Revoked\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Old tokens are invalidated on rotation\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-model\u0022\u003E\uD83D\uDD10 Security Model\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022rotation\u0022\u003E\uD83D\uDD01 Rotation\u003C/h3\u003E\n\u003Cp\u003EEach refresh:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EInvalidates previous token\u003C/li\u003E\n\u003Cli\u003EIssues a new token\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Prevents replay attacks\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022reuse-detection\u0022\u003E\uD83D\uDEA8 Reuse Detection\u003C/h3\u003E\n\u003Cp\u003EIf a token is reused:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EChain may be revoked\u003C/li\u003E\n\u003Cli\u003ESession may be revoked\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Strong forward security\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022session-binding\u0022\u003E\uD83D\uDD17 Session Binding\u003C/h3\u003E\n\u003Cp\u003ERefresh tokens include:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESessionId\u003C/li\u003E\n\u003Cli\u003EChainId (optional)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Prevents cross-context usage\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022hashed-storage\u0022\u003E\uD83D\uDD12 Hashed Storage\u003C/h3\u003E\n\u003Cp\u003ETokens are:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ENever stored as plaintext\u003C/li\u003E\n\u003Cli\u003EHashed using secure algorithms\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Reduces breach impact\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022token-issuance\u0022\u003E\uD83D\uDD04 Token Issuance\u003C/h2\u003E\n\u003Cp\u003ETokens are issued during:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogin\u003C/li\u003E\n\u003Cli\u003ERefresh\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022access-token\u0022\u003EAccess Token\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EMay be opaque or JWT\u003C/li\u003E\n\u003Cli\u003EIncludes identity and optional session reference\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022refresh-token\u0022\u003ERefresh Token\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EAlways opaque\u003C/li\u003E\n\u003Cli\u003EPersisted in secure store\u003C/li\u003E\n\u003Cli\u003EUsed only for rotation\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Tokens are not identity\u003Cbr /\u003E\n\uD83D\uDC49 They are projections of a session\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession is the source of truth\u003C/li\u003E\n\u003Cli\u003ETokens are optional and mode-dependent\u003C/li\u003E\n\u003Cli\u003EOpaque tokens require server validation\u003C/li\u003E\n\u003Cli\u003EJWT tokens allow stateless access\u003C/li\u003E\n\u003Cli\u003ERefresh tokens enable secure continuation\u003C/li\u003E\n\u003Cli\u003EToken rotation ensures forward security\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to Device Management\u003C/p\u003E\n", + "Headings": [ + { + "Id": "rethinking-tokens", + "Text": "\uD83E\uDDE0 Rethinking Tokens", + "Level": 0 + }, + { + "Id": "token-types", + "Text": "\uD83E\uDDE9 Token Types", + "Level": 0 + }, + { + "Id": "opaque-tokens", + "Text": "\uD83D\uDD39 Opaque Tokens", + "Level": 1 + }, + { + "Id": "jwt-json-web-tokens", + "Text": "\uD83D\uDD39 JWT (JSON Web Tokens)", + "Level": 1 + }, + { + "Id": "mode-based-behavior", + "Text": "\u2696\uFE0F Mode-Based Behavior", + "Level": 0 + }, + { + "Id": "access-tokens", + "Text": "\uD83D\uDD11 Access Tokens", + "Level": 0 + }, + { + "Id": "characteristics", + "Text": "Characteristics", + "Level": 1 + }, + { + "Id": "important", + "Text": "Important", + "Level": 1 + }, + { + "Id": "refresh-tokens", + "Text": "\uD83D\uDD04 Refresh Tokens", + "Level": 0 + }, + { + "Id": "characteristics-1", + "Text": "Characteristics", + "Level": 1 + }, + { + "Id": "lifecycle", + "Text": "Lifecycle", + "Level": 1 + }, + { + "Id": "security-model", + "Text": "\uD83D\uDD10 Security Model", + "Level": 0 + }, + { + "Id": "rotation", + "Text": "\uD83D\uDD01 Rotation", + "Level": 1 + }, + { + "Id": "reuse-detection", + "Text": "\uD83D\uDEA8 Reuse Detection", + "Level": 1 + }, + { + "Id": "session-binding", + "Text": "\uD83D\uDD17 Session Binding", + "Level": 1 + }, + { + "Id": "hashed-storage", + "Text": "\uD83D\uDD12 Hashed Storage", + "Level": 1 + }, + { + "Id": "token-issuance", + "Text": "\uD83D\uDD04 Token Issuance", + "Level": 0 + }, + { + "Id": "access-token", + "Text": "Access Token", + "Level": 1 + }, + { + "Id": "refresh-token", + "Text": "Refresh Token", + "Level": 1 + }, + { + "Id": "mental-model", + "Text": "\uD83E\uDDE0 Mental Model", + "Level": 0 + }, + { + "Id": "key-takeaways", + "Text": "\uD83D\uDCCC Key Takeaways", + "Level": 0 + }, + { + "Id": "next-step", + "Text": "\u27A1\uFE0F Next Step", + "Level": 0 + } + ] } \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/authentication.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/authentication.json index a6c35b2b..861594bd 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/authentication.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/authentication.json @@ -1,5 +1,72 @@ { "Slug": "client/authentication", "Title": "Authentication", - "Html": "\n\u003Cp\u003EThis section explains how to use the UltimateAuth client for authentication flows.\u003C/p\u003E\n\u003Ch2 id=\u0022overview\u0022\u003E\uD83E\uDDE0 Overview\u003C/h2\u003E\n\u003Cp\u003EAuthentication in UltimateAuth is \u003Cstrong\u003Eflow-based\u003C/strong\u003E, not endpoint-based.\u003C/p\u003E\n\u003Cp\u003EYou interact with:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ccode\u003EFlowClient\u003C/code\u003E\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch2 id=\u0022login\u0022\u003E\uD83D\uDD11 Login\u003C/h2\u003E\n\u003Ch3 id=\u0022basic-login\u0022\u003EBasic Login\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Flows.LoginAsync(new LoginRequest\n{\n Identifier = \u0026quot;user@ultimateauth.com\u0026quot;,\n Secret = \u0026quot;password\u0026quot;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This triggers a full login flow:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESends credentials\u003C/li\u003E\n\u003Cli\u003EHandles redirect\u003C/li\u003E\n\u003Cli\u003EEstablishes session\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Ch2 id=\u0022try-login-pre-check\u0022\u003E\u26A1 Try Login (Pre-check)\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Evar result = await UAuthClient.Flows.TryLoginAsync(\n new LoginRequest\n {\n Identifier = \u0026quot;user@mail.com\u0026quot;,\n Secret = \u0026quot;password\u0026quot;\n },\n UAuthSubmitMode.TryOnly\n);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022modes\u0022\u003EModes\u003C/h3\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EMode\u003C/th\u003E\n\u003Cth\u003EBehavior\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ETryOnly\u003C/td\u003E\n\u003Ctd\u003EValidate only\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EDirectCommit\u003C/td\u003E\n\u003Ctd\u003ESkip validation\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ETryAndCommit\u003C/td\u003E\n\u003Ctd\u003EValidate then login if success\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Cp\u003E\uD83D\uDC49 Use \u003Ccode\u003EDirectCommit\u003C/code\u003E when:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EYou need maximum performance while sacrificing interactive SPA capabilities.\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Use \u003Ccode\u003ETryOnly\u003C/code\u003E when:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EYou need validation feedback\u003C/li\u003E\n\u003Cli\u003EYou want custom UI flows\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Use \u003Ccode\u003ETryAndCommit\u003C/code\u003E when:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EYou need completely interactive SPA experience.\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ccode\u003ETryAndCommit\u003C/code\u003E is the recommended mode for most applications.\u003C/p\u003E\n\u003Cp\u003EIt provides:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EValidation feedback\u003C/li\u003E\n\u003Cli\u003EAutomatic redirect on success\u003C/li\u003E\n\u003Cli\u003ESmooth SPA experience\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022refresh\u0022\u003E\uD83D\uDD04 Refresh\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Evar result = await UAuthClient.Flows.RefreshAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022possible-outcomes\u0022\u003EPossible Outcomes\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ESuccess \u2192 new tokens/session updated\u003C/li\u003E\n\u003Cli\u003ETouched \u2192 session extended\u003C/li\u003E\n\u003Cli\u003ERotated \u2192 refresh token rotated\u003C/li\u003E\n\u003Cli\u003ENoOp \u2192 nothing changed\u003C/li\u003E\n\u003Cli\u003EReauthRequired \u2192 login required\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Refresh behavior depends on auth mode:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EPureOpaque \u2192 session touch\u003C/li\u003E\n\u003Cli\u003EHybrid/JWT \u2192 token rotation\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EIn default, successful refresh returns success outcome. If you want to learn success detail such as no-op, touched or rotated, open it via server options:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthServer(o =\u0026gt;\n{\n o.Diagnostics.EnableRefreshDetails = true;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022logout\u0022\u003E\uD83D\uDEAA Logout\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Flows.LogoutAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEnds current session\u003C/li\u003E\n\u003Cli\u003EClears authentication state\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022device-logout-variants\u0022\u003E\uD83D\uDCF1 Device Logout Variants\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Flows.LogoutMyDeviceAsync(sessionId);\nawait UAuthClient.Flows.LogoutMyOtherDevicesAsync();\nawait UAuthClient.Flows.LogoutAllMyDevicesAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 These operate on \u003Cstrong\u003Esession chains (devices)\u003C/strong\u003E\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022pkce-flow-public-clients\u0022\u003E\uD83D\uDD10 PKCE Flow (Public Clients)\u003C/h2\u003E\n\u003Ch3 id=\u0022start-pkce\u0022\u003EStart PKCE\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Flows.BeginPkceAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022complete-pkce\u0022\u003EComplete PKCE\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Flows.CompletePkceLoginAsync(request);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003EComplete PKCE also has try semantics the same as login flow. UltimateAuth suggests to use \u003Ccode\u003ETryCompletePkceLoginAsync\u003C/code\u003E for complete interactive experience.\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cp\u003E\uD83D\uDC49 Required for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESPA\u003C/li\u003E\n\u003Cli\u003EBlazor WASM\u003C/li\u003E\n\u003Cli\u003EMobile apps\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Ch2 id=\u0022security-note\u0022\u003E\uD83D\uDEA8 Security Note\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EPublic clients MUST use PKCE\u003C/li\u003E\n\u003Cli\u003EServer clients MAY allow direct login\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EDirect credential posting disabled by default and throws exception when you directly call login. You can enable it via options. You should only use it for debugging and development purposes.\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthClientBlazor(o =\u0026gt;\n{\n o.Login.AllowCredentialPost = true;\n});\n---\n\n## \uD83C\uDFAF Summary\n\nAuthentication in UltimateAuth:\n\n- is flow-driven\n- adapts to client type\n- enforces security by design\n\n---\n\n\uD83D\uDC49 Always use \u0060FlowClient\u0060 for authentication operations\n\u003C/code\u003E\u003C/pre\u003E\n" + "Html": "\n\u003Cp\u003EThis section explains how to use the UltimateAuth client for authentication flows.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022overview\u0022\u003E\uD83E\uDDE0 Overview\u003C/h2\u003E\n\u003Cp\u003EAuthentication in UltimateAuth is \u003Cstrong\u003Eflow-based\u003C/strong\u003E, not endpoint-based.\u003C/p\u003E\n\u003Cp\u003EYou interact with:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ccode\u003EFlowClient\u003C/code\u003E\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022login\u0022\u003E\uD83D\uDD11 Login\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022basic-login\u0022\u003EBasic Login\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Flows.LoginAsync(new LoginRequest\n{\n Identifier = \u0026quot;user@ultimateauth.com\u0026quot;,\n Secret = \u0026quot;password\u0026quot;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This triggers a full login flow:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESends credentials\u003C/li\u003E\n\u003Cli\u003EHandles redirect\u003C/li\u003E\n\u003Cli\u003EEstablishes session\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022try-login-pre-check\u0022\u003E\u26A1 Try Login (Pre-check)\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Evar result = await UAuthClient.Flows.TryLoginAsync(\n new LoginRequest\n {\n Identifier = \u0026quot;user@mail.com\u0026quot;,\n Secret = \u0026quot;password\u0026quot;\n },\n UAuthSubmitMode.TryOnly\n);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022modes\u0022\u003EModes\u003C/h3\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EMode\u003C/th\u003E\n\u003Cth\u003EBehavior\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ETryOnly\u003C/td\u003E\n\u003Ctd\u003EValidate only\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EDirectCommit\u003C/td\u003E\n\u003Ctd\u003ESkip validation\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ETryAndCommit\u003C/td\u003E\n\u003Ctd\u003EValidate then login if success\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Cp\u003E\uD83D\uDC49 Use \u003Ccode\u003EDirectCommit\u003C/code\u003E when:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EYou need maximum performance while sacrificing interactive SPA capabilities.\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Use \u003Ccode\u003ETryOnly\u003C/code\u003E when:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EYou need validation feedback\u003C/li\u003E\n\u003Cli\u003EYou want custom UI flows\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Use \u003Ccode\u003ETryAndCommit\u003C/code\u003E when:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EYou need completely interactive SPA experience.\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ccode\u003ETryAndCommit\u003C/code\u003E is the recommended mode for most applications.\u003C/p\u003E\n\u003Cp\u003EIt provides:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EValidation feedback\u003C/li\u003E\n\u003Cli\u003EAutomatic redirect on success\u003C/li\u003E\n\u003Cli\u003ESmooth SPA experience\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022refresh\u0022\u003E\uD83D\uDD04 Refresh\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Evar result = await UAuthClient.Flows.RefreshAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022possible-outcomes\u0022\u003EPossible Outcomes\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ESuccess \u2192 new tokens/session updated\u003C/li\u003E\n\u003Cli\u003ETouched \u2192 session extended\u003C/li\u003E\n\u003Cli\u003ERotated \u2192 refresh token rotated\u003C/li\u003E\n\u003Cli\u003ENoOp \u2192 nothing changed\u003C/li\u003E\n\u003Cli\u003EReauthRequired \u2192 login required\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Refresh behavior depends on auth mode:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EPureOpaque \u2192 session touch\u003C/li\u003E\n\u003Cli\u003EHybrid/JWT \u2192 token rotation\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EIn default, successful refresh returns success outcome. If you want to learn success detail such as no-op, touched or rotated, open it via server options:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthServer(o =\u0026gt;\n{\n o.Diagnostics.EnableRefreshDetails = true;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022logout\u0022\u003E\uD83D\uDEAA Logout\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Flows.LogoutAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEnds current session\u003C/li\u003E\n\u003Cli\u003EClears authentication state\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022device-logout-variants\u0022\u003E\uD83D\uDCF1 Device Logout Variants\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Flows.LogoutMyDeviceAsync(sessionId);\nawait UAuthClient.Flows.LogoutMyOtherDevicesAsync();\nawait UAuthClient.Flows.LogoutAllMyDevicesAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 These operate on \u003Cstrong\u003Esession chains (devices)\u003C/strong\u003E\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022pkce-flow-public-clients\u0022\u003E\uD83D\uDD10 PKCE Flow (Public Clients)\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022start-pkce\u0022\u003EStart PKCE\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Flows.BeginPkceAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022complete-pkce\u0022\u003EComplete PKCE\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Flows.CompletePkceLoginAsync(request);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003EComplete PKCE also has try semantics the same as login flow. UltimateAuth suggests to use \u003Ccode\u003ETryCompletePkceLoginAsync\u003C/code\u003E for complete interactive experience.\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cp\u003E\uD83D\uDC49 Required for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESPA\u003C/li\u003E\n\u003Cli\u003EBlazor WASM\u003C/li\u003E\n\u003Cli\u003EMobile apps\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-note\u0022\u003E\uD83D\uDEA8 Security Note\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EPublic clients MUST use PKCE\u003C/li\u003E\n\u003Cli\u003EServer clients MAY allow direct login\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EDirect credential posting disabled by default and throws exception when you directly call login. You can enable it via options. You should only use it for debugging and development purposes.\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthClientBlazor(o =\u0026gt;\n{\n o.Login.AllowCredentialPost = true;\n});\n---\n\n## \uD83C\uDFAF Summary\n\nAuthentication in UltimateAuth:\n\n- is flow-driven\n- adapts to client type\n- enforces security by design\n\n---\n\n\uD83D\uDC49 Always use \u0060FlowClient\u0060 for authentication operations\n\u003C/code\u003E\u003C/pre\u003E\n", + "Headings": [ + { + "Id": "overview", + "Text": "\uD83E\uDDE0 Overview", + "Level": 0 + }, + { + "Id": "login", + "Text": "\uD83D\uDD11 Login", + "Level": 0 + }, + { + "Id": "basic-login", + "Text": "Basic Login", + "Level": 1 + }, + { + "Id": "try-login-pre-check", + "Text": "\u26A1 Try Login (Pre-check)", + "Level": 0 + }, + { + "Id": "modes", + "Text": "Modes", + "Level": 1 + }, + { + "Id": "refresh", + "Text": "\uD83D\uDD04 Refresh", + "Level": 0 + }, + { + "Id": "possible-outcomes", + "Text": "Possible Outcomes", + "Level": 1 + }, + { + "Id": "logout", + "Text": "\uD83D\uDEAA Logout", + "Level": 0 + }, + { + "Id": "device-logout-variants", + "Text": "\uD83D\uDCF1 Device Logout Variants", + "Level": 0 + }, + { + "Id": "pkce-flow-public-clients", + "Text": "\uD83D\uDD10 PKCE Flow (Public Clients)", + "Level": 0 + }, + { + "Id": "start-pkce", + "Text": "Start PKCE", + "Level": 1 + }, + { + "Id": "complete-pkce", + "Text": "Complete PKCE", + "Level": 1 + }, + { + "Id": "security-note", + "Text": "\uD83D\uDEA8 Security Note", + "Level": 0 + } + ] } \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/authorization.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/authorization.json index d8dc663e..9d9d909b 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/authorization.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/authorization.json @@ -1,5 +1,72 @@ { "Slug": "client/authorization", "Title": "Authorization", - "Html": "\n\u003Cp\u003EThis section explains how to manage roles, permissions, and access control using the UltimateAuth client.\u003C/p\u003E\n\u003Ch2 id=\u0022overview\u0022\u003E\uD83E\uDDE0 Overview\u003C/h2\u003E\n\u003Cp\u003EAuthorization in UltimateAuth is policy-driven and role-based.\u003C/p\u003E\n\u003Cp\u003EOn the client, you interact with:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003EUAuthClient.Authorization\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022core-concepts\u0022\u003E\uD83D\uDD11 Core Concepts\u003C/h2\u003E\n\u003Cp\u003ERoles\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ENamed groups of permissions\u003C/li\u003E\n\u003Cli\u003EAssigned to users\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EPermissions\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EFine-grained access definitions\u003C/li\u003E\n\u003Cli\u003EExample: users.create.anonymous, users.delete.self, authorization.roles.admin\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EPolicies\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERuntime decision rules\u003C/li\u003E\n\u003Cli\u003EEnforced automatically on server\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch3 id=\u0022query-roles\u0022\u003E\uD83D\uDCCB Query Roles\u003C/h3\u003E\n\u003Cp\u003Evar result = await UAuthClient.Authorization.QueryRolesAsync(new RoleQuery\n);\u003C/p\u003E\n\u003Ch3 id=\u0022create-role\u0022\u003E\u2795 Create Role\u003C/h3\u003E\n\u003Cp\u003Eawait UAuthClient.Authorization.CreateRoleAsync(new CreateRoleRequest\n);\u003C/p\u003E\n\u003Ch3 id=\u0022rename-role\u0022\u003E\u270F\uFE0F Rename Role\u003C/h3\u003E\n\u003Cp\u003Eawait UAuthClient.Authorization.RenameRoleAsync(new RenameRoleRequest\n);\u003C/p\u003E\n\u003Ch3 id=\u0022set-permissions\u0022\u003E\uD83E\uDDE9 Set Permissions\u003C/h3\u003E\n\u003Cp\u003Eawait UAuthClient.Authorization.SetRolePermissionsAsync(new SetRolePermissionsRequest\n{\nRoleId = roleId,\nPermissions = new[]\n{\nPermission.From(\u0026quot;users.read\u0026quot;),\nPermission.From(\u0026quot;users.update\u0026quot;)\n}\n});\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Permissions support:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EExact match \u2192 users.create\u003C/li\u003E\n\u003Cli\u003EPrefix \u2192 users.*\u003C/li\u003E\n\u003Cli\u003EWildcard \u2192 *\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022delete-role\u0022\u003E\u274C Delete Role\u003C/h3\u003E\n\u003Cp\u003Eawait UAuthClient.Authorization.DeleteRoleAsync(new DeleteRoleRequest\n);\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Automatically removes role assignments from users\u003C/p\u003E\n\u003Ch3 id=\u0022assign-role-to-user\u0022\u003E\uD83D\uDC64 Assign Role to User\u003C/h3\u003E\n\u003Cp\u003Eawait UAuthClient.Authorization.AssignRoleToUserAsync(new AssignRoleRequest\n);\u003C/p\u003E\n\u003Ch3 id=\u0022remove-role\u0022\u003E\u2796 Remove Role\u003C/h3\u003E\n\u003Cp\u003Eawait UAuthClient.Authorization.RemoveRoleFromUserAsync(new RemoveRoleRequest\n);\u003C/p\u003E\n\u003Ch3 id=\u0022get-user-roles\u0022\u003E\uD83D\uDCCB Get User Roles\u003C/h3\u003E\n\u003Cp\u003Evar roles = await UAuthClient.Authorization.GetUserRolesAsync(userKey);\n\uD83D\uDD0D Check Authorization\nvar result = await UAuthClient.Authorization.CheckAsync(new AuthorizationCheckRequest\n);\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Returns:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAllow / Deny\u003C/li\u003E\n\u003Cli\u003EReason (if denied)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022permission-model\u0022\u003E\uD83E\uDDE0 Permission Model\u003C/h2\u003E\n\u003Cp\u003EPermissions are normalized and optimized:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EFull access \u2192 *\u003C/li\u003E\n\u003Cli\u003EGroup access \u2192 users.*\u003C/li\u003E\n\u003Cli\u003ESpecific \u2192 users.create\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Internally compiled for fast evaluation\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022security-notes\u0022\u003E\uD83D\uDD10 Security Notes\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EAuthorization is enforced server-side\u003C/li\u003E\n\u003Cli\u003EClient only requests actions\u003C/li\u003E\n\u003Cli\u003EPolicies may override permissions\u003C/li\u003E\n\u003Cli\u003ECross-tenant access is denied by default\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022summary\u0022\u003E\uD83C\uDFAF Summary\u003C/h2\u003E\n\u003Cp\u003EAuthorization in UltimateAuth:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Ecombines roles \u002B permissions \u002B policies\u003C/li\u003E\n\u003Cli\u003Eis evaluated through a decision pipeline\u003C/li\u003E\n\u003Cli\u003Esupports fine-grained and scalable access control\u003C/li\u003E\n\u003C/ul\u003E\n" + "Html": "\n\u003Cp\u003EThis section explains how to manage roles, permissions, and access control using the UltimateAuth client.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022overview\u0022\u003E\uD83E\uDDE0 Overview\u003C/h2\u003E\n\u003Cp\u003EAuthorization in UltimateAuth is policy-driven and role-based.\u003C/p\u003E\n\u003Cp\u003EOn the client, you interact with:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003EUAuthClient.Authorization\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022core-concepts\u0022\u003E\uD83D\uDD11 Core Concepts\u003C/h2\u003E\n\u003Cp\u003ERoles\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ENamed groups of permissions\u003C/li\u003E\n\u003Cli\u003EAssigned to users\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EPermissions\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EFine-grained access definitions\u003C/li\u003E\n\u003Cli\u003EExample: users.create.anonymous, users.delete.self, authorization.roles.admin\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EPolicies\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERuntime decision rules\u003C/li\u003E\n\u003Cli\u003EEnforced automatically on server\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022query-roles\u0022\u003E\uD83D\uDCCB Query Roles\u003C/h3\u003E\n\u003Cp\u003Evar result = await UAuthClient.Authorization.QueryRolesAsync(new RoleQuery\n);\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022create-role\u0022\u003E\u2795 Create Role\u003C/h3\u003E\n\u003Cp\u003Eawait UAuthClient.Authorization.CreateRoleAsync(new CreateRoleRequest\n);\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022rename-role\u0022\u003E\u270F\uFE0F Rename Role\u003C/h3\u003E\n\u003Cp\u003Eawait UAuthClient.Authorization.RenameRoleAsync(new RenameRoleRequest\n);\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022set-permissions\u0022\u003E\uD83E\uDDE9 Set Permissions\u003C/h3\u003E\n\u003Cp\u003Eawait UAuthClient.Authorization.SetRolePermissionsAsync(new SetRolePermissionsRequest\n{\nRoleId = roleId,\nPermissions = new[]\n{\nPermission.From(\u0026quot;users.read\u0026quot;),\nPermission.From(\u0026quot;users.update\u0026quot;)\n}\n});\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Permissions support:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EExact match \u2192 users.create\u003C/li\u003E\n\u003Cli\u003EPrefix \u2192 users.*\u003C/li\u003E\n\u003Cli\u003EWildcard \u2192 *\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022delete-role\u0022\u003E\u274C Delete Role\u003C/h3\u003E\n\u003Cp\u003Eawait UAuthClient.Authorization.DeleteRoleAsync(new DeleteRoleRequest\n);\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Automatically removes role assignments from users\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022assign-role-to-user\u0022\u003E\uD83D\uDC64 Assign Role to User\u003C/h3\u003E\n\u003Cp\u003Eawait UAuthClient.Authorization.AssignRoleToUserAsync(new AssignRoleRequest\n);\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022remove-role\u0022\u003E\u2796 Remove Role\u003C/h3\u003E\n\u003Cp\u003Eawait UAuthClient.Authorization.RemoveRoleFromUserAsync(new RemoveRoleRequest\n);\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022get-user-roles\u0022\u003E\uD83D\uDCCB Get User Roles\u003C/h3\u003E\n\u003Cp\u003Evar roles = await UAuthClient.Authorization.GetUserRolesAsync(userKey);\n\uD83D\uDD0D Check Authorization\nvar result = await UAuthClient.Authorization.CheckAsync(new AuthorizationCheckRequest\n);\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Returns:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAllow / Deny\u003C/li\u003E\n\u003Cli\u003EReason (if denied)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022permission-model\u0022\u003E\uD83E\uDDE0 Permission Model\u003C/h2\u003E\n\u003Cp\u003EPermissions are normalized and optimized:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EFull access \u2192 *\u003C/li\u003E\n\u003Cli\u003EGroup access \u2192 users.*\u003C/li\u003E\n\u003Cli\u003ESpecific \u2192 users.create\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Internally compiled for fast evaluation\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-notes\u0022\u003E\uD83D\uDD10 Security Notes\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EAuthorization is enforced server-side\u003C/li\u003E\n\u003Cli\u003EClient only requests actions\u003C/li\u003E\n\u003Cli\u003EPolicies may override permissions\u003C/li\u003E\n\u003Cli\u003ECross-tenant access is denied by default\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022summary\u0022\u003E\uD83C\uDFAF Summary\u003C/h2\u003E\n\u003Cp\u003EAuthorization in UltimateAuth:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Ecombines roles \u002B permissions \u002B policies\u003C/li\u003E\n\u003Cli\u003Eis evaluated through a decision pipeline\u003C/li\u003E\n\u003Cli\u003Esupports fine-grained and scalable access control\u003C/li\u003E\n\u003C/ul\u003E\n", + "Headings": [ + { + "Id": "overview", + "Text": "\uD83E\uDDE0 Overview", + "Level": 0 + }, + { + "Id": "core-concepts", + "Text": "\uD83D\uDD11 Core Concepts", + "Level": 0 + }, + { + "Id": "query-roles", + "Text": "\uD83D\uDCCB Query Roles", + "Level": 1 + }, + { + "Id": "create-role", + "Text": "\u2795 Create Role", + "Level": 1 + }, + { + "Id": "rename-role", + "Text": "\u270F\uFE0F Rename Role", + "Level": 1 + }, + { + "Id": "set-permissions", + "Text": "\uD83E\uDDE9 Set Permissions", + "Level": 1 + }, + { + "Id": "delete-role", + "Text": "\u274C Delete Role", + "Level": 1 + }, + { + "Id": "assign-role-to-user", + "Text": "\uD83D\uDC64 Assign Role to User", + "Level": 1 + }, + { + "Id": "remove-role", + "Text": "\u2796 Remove Role", + "Level": 1 + }, + { + "Id": "get-user-roles", + "Text": "\uD83D\uDCCB Get User Roles", + "Level": 1 + }, + { + "Id": "permission-model", + "Text": "\uD83E\uDDE0 Permission Model", + "Level": 0 + }, + { + "Id": "security-notes", + "Text": "\uD83D\uDD10 Security Notes", + "Level": 0 + }, + { + "Id": "summary", + "Text": "\uD83C\uDFAF Summary", + "Level": 0 + } + ] } \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/credentials.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/credentials.json index 631ec06d..429aa532 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/credentials.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/credentials.json @@ -1,5 +1,92 @@ { "Slug": "client/credentials", "Title": "Credentials", - "Html": "\n\u003Cp\u003EThis section explains how to manage user credentials (such as passwords) using the UltimateAuth client.\u003C/p\u003E\n\u003Ch2 id=\u0022overview\u0022\u003E\uD83E\uDDE0 Overview\u003C/h2\u003E\n\u003Cp\u003ECredential operations are handled via:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003EUAuthClient.Credentials...\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThis includes:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Epassword management\u003C/li\u003E\n\u003Cli\u003Ecredential reset flows\u003C/li\u003E\n\u003Cli\u003Eadmin credential operations\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022change-password-self\u0022\u003E\uD83D\uDD10 Change Password (Self)\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Credentials.ChangeMyAsync(new ChangeCredentialRequest\n{\n CurrentSecret = \u0026quot;old-password\u0026quot;,\n NewSecret = \u0026quot;new-password\u0026quot;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Requires current password\u003Cbr /\u003E\n\uD83D\uDC49 Triggers \u003Ccode\u003ECredentialsChangedSelf\u003C/code\u003E event\u003C/p\u003E\n\u003Ch2 id=\u0022reset-password-self\u0022\u003E\uD83D\uDD01 Reset Password (Self)\u003C/h2\u003E\n\u003Ch3 id=\u0022begin-reset\u0022\u003EBegin Reset\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Evar begin = await UAuthClient.Credentials.BeginResetMyAsync(\n new BeginResetCredentialRequest\n {\n Identifier = \u0026quot;user@mail.com\u0026quot;\n });\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022complete-reset\u0022\u003EComplete Reset\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Credentials.CompleteResetMyAsync(\n new CompleteResetCredentialRequest\n {\n Token = \u0026quot;reset-token\u0026quot;,\n NewSecret = \u0026quot;new-password\u0026quot;\n });\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Typically used in:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eforgot password flows\u003C/li\u003E\n\u003Cli\u003Eemail-based reset flows\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022add-credential-self\u0022\u003E\u2795 Add Credential (Self)\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Credentials.AddMyAsync(new AddCredentialRequest\n{\n Secret = \u0026quot;password\u0026quot;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022revoke-credential-self\u0022\u003E\u274C Revoke Credential (Self)\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Credentials.RevokeMyAsync(new RevokeCredentialRequest\n{\n CredentialId = credentialId\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Useful for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eremoving login methods\u003C/li\u003E\n\u003Cli\u003Einvalidating compromised credentials\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022admin-change-user-credential\u0022\u003E\uD83D\uDC51 Admin: Change User Credential\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Credentials.ChangeUserAsync(userKey, new ChangeCredentialRequest\n{\n NewSecret = \u0026quot;new-password\u0026quot;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Does NOT require current password\u003C/p\u003E\n\u003Ch2 id=\u0022admin-add-credential\u0022\u003E\uD83D\uDC51 Admin: Add Credential\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Credentials.AddUserAsync(userKey, new AddCredentialRequest\n{\n Secret = \u0026quot;password\u0026quot;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022admin-revoke-credential\u0022\u003E\uD83D\uDC51 Admin: Revoke Credential\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Credentials.RevokeUserAsync(userKey, new RevokeCredentialRequest\n{\n CredentialId = credentialId\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022admin-reset-credential\u0022\u003E\uD83D\uDC51 Admin: Reset Credential\u003C/h2\u003E\n\u003Ch3 id=\u0022begin\u0022\u003EBegin\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Credentials.BeginResetUserAsync(userKey, request);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022complete\u0022\u003EComplete\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Credentials.CompleteResetUserAsync(userKey, request);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022delete-credential-admin\u0022\u003E\u274C Delete Credential (Admin)\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Credentials.DeleteUserAsync(userKey, new DeleteCredentialRequest\n{\n CredentialId = credentialId\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022credential-model\u0022\u003E\uD83E\uDDE0 Credential Model\u003C/h2\u003E\n\u003Cp\u003ECredentials are:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Euser-bound\u003C/li\u003E\n\u003Cli\u003Esecurity-version aware\u003C/li\u003E\n\u003Cli\u003Elifecycle-managed\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Changing credentials may:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Einvalidate sessions\u003C/li\u003E\n\u003Cli\u003Etrigger security updates\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022security-notes\u0022\u003E\uD83D\uDD10 Security Notes\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EPasswords are never sent back to client\u003C/li\u003E\n\u003Cli\u003EAll secrets are hashed server-side\u003C/li\u003E\n\u003Cli\u003EReset flows should be protected (email, OTP, etc.)\u003C/li\u003E\n\u003Cli\u003EAdmin operations are policy-protected\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022summary\u0022\u003E\uD83C\uDFAF Summary\u003C/h2\u003E\n\u003Cp\u003ECredential management in UltimateAuth:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Esupports self-service and admin flows\u003C/li\u003E\n\u003Cli\u003Eintegrates with security lifecycle\u003C/li\u003E\n\u003Cli\u003Eenables safe credential rotation\u003C/li\u003E\n\u003C/ul\u003E\n" + "Html": "\n\u003Cp\u003EThis section explains how to manage user credentials (such as passwords) using the UltimateAuth client.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022overview\u0022\u003E\uD83E\uDDE0 Overview\u003C/h2\u003E\n\u003Cp\u003ECredential operations are handled via:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003EUAuthClient.Credentials...\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThis includes:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Epassword management\u003C/li\u003E\n\u003Cli\u003Ecredential reset flows\u003C/li\u003E\n\u003Cli\u003Eadmin credential operations\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022change-password-self\u0022\u003E\uD83D\uDD10 Change Password (Self)\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Credentials.ChangeMyAsync(new ChangeCredentialRequest\n{\n CurrentSecret = \u0026quot;old-password\u0026quot;,\n NewSecret = \u0026quot;new-password\u0026quot;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Requires current password\u003Cbr /\u003E\n\uD83D\uDC49 Triggers \u003Ccode\u003ECredentialsChangedSelf\u003C/code\u003E event\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022reset-password-self\u0022\u003E\uD83D\uDD01 Reset Password (Self)\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022begin-reset\u0022\u003EBegin Reset\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Evar begin = await UAuthClient.Credentials.BeginResetMyAsync(\n new BeginResetCredentialRequest\n {\n Identifier = \u0026quot;user@mail.com\u0026quot;\n });\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022complete-reset\u0022\u003EComplete Reset\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Credentials.CompleteResetMyAsync(\n new CompleteResetCredentialRequest\n {\n Token = \u0026quot;reset-token\u0026quot;,\n NewSecret = \u0026quot;new-password\u0026quot;\n });\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Typically used in:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eforgot password flows\u003C/li\u003E\n\u003Cli\u003Eemail-based reset flows\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022add-credential-self\u0022\u003E\u2795 Add Credential (Self)\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Credentials.AddMyAsync(new AddCredentialRequest\n{\n Secret = \u0026quot;password\u0026quot;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022revoke-credential-self\u0022\u003E\u274C Revoke Credential (Self)\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Credentials.RevokeMyAsync(new RevokeCredentialRequest\n{\n CredentialId = credentialId\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Useful for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eremoving login methods\u003C/li\u003E\n\u003Cli\u003Einvalidating compromised credentials\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022admin-change-user-credential\u0022\u003E\uD83D\uDC51 Admin: Change User Credential\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Credentials.ChangeUserAsync(userKey, new ChangeCredentialRequest\n{\n NewSecret = \u0026quot;new-password\u0026quot;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Does NOT require current password\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022admin-add-credential\u0022\u003E\uD83D\uDC51 Admin: Add Credential\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Credentials.AddUserAsync(userKey, new AddCredentialRequest\n{\n Secret = \u0026quot;password\u0026quot;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022admin-revoke-credential\u0022\u003E\uD83D\uDC51 Admin: Revoke Credential\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Credentials.RevokeUserAsync(userKey, new RevokeCredentialRequest\n{\n CredentialId = credentialId\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022admin-reset-credential\u0022\u003E\uD83D\uDC51 Admin: Reset Credential\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022begin\u0022\u003EBegin\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Credentials.BeginResetUserAsync(userKey, request);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022complete\u0022\u003EComplete\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Credentials.CompleteResetUserAsync(userKey, request);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022delete-credential-admin\u0022\u003E\u274C Delete Credential (Admin)\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Credentials.DeleteUserAsync(userKey, new DeleteCredentialRequest\n{\n CredentialId = credentialId\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022credential-model\u0022\u003E\uD83E\uDDE0 Credential Model\u003C/h2\u003E\n\u003Cp\u003ECredentials are:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Euser-bound\u003C/li\u003E\n\u003Cli\u003Esecurity-version aware\u003C/li\u003E\n\u003Cli\u003Elifecycle-managed\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Changing credentials may:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Einvalidate sessions\u003C/li\u003E\n\u003Cli\u003Etrigger security updates\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-notes\u0022\u003E\uD83D\uDD10 Security Notes\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EPasswords are never sent back to client\u003C/li\u003E\n\u003Cli\u003EAll secrets are hashed server-side\u003C/li\u003E\n\u003Cli\u003EReset flows should be protected (email, OTP, etc.)\u003C/li\u003E\n\u003Cli\u003EAdmin operations are policy-protected\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022summary\u0022\u003E\uD83C\uDFAF Summary\u003C/h2\u003E\n\u003Cp\u003ECredential management in UltimateAuth:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Esupports self-service and admin flows\u003C/li\u003E\n\u003Cli\u003Eintegrates with security lifecycle\u003C/li\u003E\n\u003Cli\u003Eenables safe credential rotation\u003C/li\u003E\n\u003C/ul\u003E\n", + "Headings": [ + { + "Id": "overview", + "Text": "\uD83E\uDDE0 Overview", + "Level": 0 + }, + { + "Id": "change-password-self", + "Text": "\uD83D\uDD10 Change Password (Self)", + "Level": 0 + }, + { + "Id": "reset-password-self", + "Text": "\uD83D\uDD01 Reset Password (Self)", + "Level": 0 + }, + { + "Id": "begin-reset", + "Text": "Begin Reset", + "Level": 1 + }, + { + "Id": "complete-reset", + "Text": "Complete Reset", + "Level": 1 + }, + { + "Id": "add-credential-self", + "Text": "\u2795 Add Credential (Self)", + "Level": 0 + }, + { + "Id": "revoke-credential-self", + "Text": "\u274C Revoke Credential (Self)", + "Level": 0 + }, + { + "Id": "admin-change-user-credential", + "Text": "\uD83D\uDC51 Admin: Change User Credential", + "Level": 0 + }, + { + "Id": "admin-add-credential", + "Text": "\uD83D\uDC51 Admin: Add Credential", + "Level": 0 + }, + { + "Id": "admin-revoke-credential", + "Text": "\uD83D\uDC51 Admin: Revoke Credential", + "Level": 0 + }, + { + "Id": "admin-reset-credential", + "Text": "\uD83D\uDC51 Admin: Reset Credential", + "Level": 0 + }, + { + "Id": "begin", + "Text": "Begin", + "Level": 1 + }, + { + "Id": "complete", + "Text": "Complete", + "Level": 1 + }, + { + "Id": "delete-credential-admin", + "Text": "\u274C Delete Credential (Admin)", + "Level": 0 + }, + { + "Id": "credential-model", + "Text": "\uD83E\uDDE0 Credential Model", + "Level": 0 + }, + { + "Id": "security-notes", + "Text": "\uD83D\uDD10 Security Notes", + "Level": 0 + }, + { + "Id": "summary", + "Text": "\uD83C\uDFAF Summary", + "Level": 0 + } + ] } \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/identifiers.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/identifiers.json index d413e344..73b092d4 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/identifiers.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/identifiers.json @@ -1,5 +1,92 @@ { "Slug": "client/identifiers", "Title": "Identifiers", - "Html": "\n\u003Cp\u003EThis section explains how UltimateAuth manages user identifiers such as email, username, and phone.\u003C/p\u003E\n\u003Ch2 id=\u0022overview\u0022\u003E\uD83E\uDDE0 Overview\u003C/h2\u003E\n\u003Cp\u003EIn UltimateAuth, identifiers are \u003Cstrong\u003Efirst-class entities\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EThey are NOT just fields on the user.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 On the client, you interact with:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003EUAuthClient.Identifiers...\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022what-is-an-identifier\u0022\u003E\uD83D\uDD11 What is an Identifier?\u003C/h2\u003E\n\u003Cp\u003EAn identifier represents a way to identify a user:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEmail\u003C/li\u003E\n\u003Cli\u003EUsername\u003C/li\u003E\n\u003Cli\u003EPhone number\u003C/li\u003E\n\u003Cli\u003ECustom identifiers\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Each identifier has:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EValue (e.g. user@ultimateauth.com)\u003C/li\u003E\n\u003Cli\u003EType (email, username, etc.)\u003C/li\u003E\n\u003Cli\u003EVerification state\u003C/li\u003E\n\u003Cli\u003EPrimary flag\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022primary-identifier\u0022\u003E\u2B50 Primary Identifier\u003C/h2\u003E\n\u003Cp\u003EA user can have multiple identifiers, but only one can be \u003Cstrong\u003Eprimary\u003C/strong\u003E. Setting an identifier to primary automatically unset the current primary identifier if exists.\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Identifiers.SetMyPrimaryAsync(new SetPrimaryUserIdentifierRequest\n{\n Id = identifierId\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Primary identifier is typically:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUsed for display\u003C/li\u003E\n\u003Cli\u003EPreferred for communication\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022login-identifiers\u0022\u003E\uD83D\uDD10 Login Identifiers\u003C/h2\u003E\n\u003Cp\u003ENot all identifiers are used for login.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Cstrong\u003ELogin identifiers are a subset of identifiers\u003C/strong\u003E\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 This is configurable:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEnable/disable per type\u003C/li\u003E\n\u003Cli\u003ECustom logic can be applied\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022get-my-identifiers\u0022\u003E\uD83D\uDCCB Get My Identifiers\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Evar result = await UAuthClient.Identifiers.GetMyAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022add-identifier\u0022\u003E\u2795 Add Identifier\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Identifiers.AddMyAsync(new AddUserIdentifierRequest\n{\n Identifier = \u0026quot;new@ultimateauth.com\u0026quot;,\n Type = UserIdentifierType.Email,\n IsPrimary = true\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022update-identifier\u0022\u003E\u270F\uFE0F Update Identifier\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003EUpdateUserIdentifierRequest updateRequest = new()\n{\n Id = item.Id,\n NewValue = item.Value\n};\n\nawait UAuthClient.Identifiers.UpdateMyAsync(updateRequest);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022verify-identifier\u0022\u003E\u2705 Verify Identifier\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Identifiers.VerifyMyAsync(new VerifyUserIdentifierRequest\n{\n Id = identifierId\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022delete-identifier\u0022\u003E\u274C Delete Identifier\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Identifiers.DeleteMyAsync(new DeleteUserIdentifierRequest\n{\n Id = identifierId\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022admin-operations\u0022\u003E\uD83D\uDC64 Admin Operations\u003C/h2\u003E\n\u003Ch3 id=\u0022get-user-identifiers\u0022\u003EGet User Identifiers\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Identifiers.GetUserAsync(userKey);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022add-identifier-to-user\u0022\u003EAdd Identifier to User\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Identifiers.AddUserAsync(userKey, request);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022update-identifier-1\u0022\u003EUpdate Identifier\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Identifiers.UpdateUserAsync(userKey, request);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022delete-identifier-1\u0022\u003EDelete Identifier\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Identifiers.DeleteUserAsync(userKey, request);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022state-events\u0022\u003E\uD83D\uDD04 State Events\u003C/h2\u003E\n\u003Cp\u003EIdentifier changes trigger events:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EIdentifiersChanged\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Useful for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUI updates\u003C/li\u003E\n\u003Cli\u003ECache invalidation\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022security-considerations\u0022\u003E\uD83D\uDD10 Security Considerations\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EIdentifiers may require verification\u003C/li\u003E\n\u003Cli\u003ELogin identifiers can be restricted\u003C/li\u003E\n\u003Cli\u003EPrimary identifier can be controlled\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022summary\u0022\u003E\uD83C\uDFAF Summary\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth identifiers:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eare independent entities\u003C/li\u003E\n\u003Cli\u003Esupport multiple types\u003C/li\u003E\n\u003Cli\u003Eseparate login vs non-login identifiers\u003C/li\u003E\n\u003Cli\u003Eare fully manageable via client\u003C/li\u003E\n\u003C/ul\u003E\n" + "Html": "\n\u003Cp\u003EThis section explains how UltimateAuth manages user identifiers such as email, username, and phone.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022overview\u0022\u003E\uD83E\uDDE0 Overview\u003C/h2\u003E\n\u003Cp\u003EIn UltimateAuth, identifiers are \u003Cstrong\u003Efirst-class entities\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EThey are NOT just fields on the user.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 On the client, you interact with:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003EUAuthClient.Identifiers...\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-is-an-identifier\u0022\u003E\uD83D\uDD11 What is an Identifier?\u003C/h2\u003E\n\u003Cp\u003EAn identifier represents a way to identify a user:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEmail\u003C/li\u003E\n\u003Cli\u003EUsername\u003C/li\u003E\n\u003Cli\u003EPhone number\u003C/li\u003E\n\u003Cli\u003ECustom identifiers\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Each identifier has:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EValue (e.g. user@ultimateauth.com)\u003C/li\u003E\n\u003Cli\u003EType (email, username, etc.)\u003C/li\u003E\n\u003Cli\u003EVerification state\u003C/li\u003E\n\u003Cli\u003EPrimary flag\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022primary-identifier\u0022\u003E\u2B50 Primary Identifier\u003C/h2\u003E\n\u003Cp\u003EA user can have multiple identifiers, but only one can be \u003Cstrong\u003Eprimary\u003C/strong\u003E. Setting an identifier to primary automatically unset the current primary identifier if exists.\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Identifiers.SetMyPrimaryAsync(new SetPrimaryUserIdentifierRequest\n{\n Id = identifierId\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Primary identifier is typically:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUsed for display\u003C/li\u003E\n\u003Cli\u003EPreferred for communication\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022login-identifiers\u0022\u003E\uD83D\uDD10 Login Identifiers\u003C/h2\u003E\n\u003Cp\u003ENot all identifiers are used for login.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Cstrong\u003ELogin identifiers are a subset of identifiers\u003C/strong\u003E\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 This is configurable:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEnable/disable per type\u003C/li\u003E\n\u003Cli\u003ECustom logic can be applied\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022get-my-identifiers\u0022\u003E\uD83D\uDCCB Get My Identifiers\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Evar result = await UAuthClient.Identifiers.GetMyAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022add-identifier\u0022\u003E\u2795 Add Identifier\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Identifiers.AddMyAsync(new AddUserIdentifierRequest\n{\n Identifier = \u0026quot;new@ultimateauth.com\u0026quot;,\n Type = UserIdentifierType.Email,\n IsPrimary = true\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022update-identifier\u0022\u003E\u270F\uFE0F Update Identifier\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003EUpdateUserIdentifierRequest updateRequest = new()\n{\n Id = item.Id,\n NewValue = item.Value\n};\n\nawait UAuthClient.Identifiers.UpdateMyAsync(updateRequest);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022verify-identifier\u0022\u003E\u2705 Verify Identifier\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Identifiers.VerifyMyAsync(new VerifyUserIdentifierRequest\n{\n Id = identifierId\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022delete-identifier\u0022\u003E\u274C Delete Identifier\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Identifiers.DeleteMyAsync(new DeleteUserIdentifierRequest\n{\n Id = identifierId\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022admin-operations\u0022\u003E\uD83D\uDC64 Admin Operations\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022get-user-identifiers\u0022\u003EGet User Identifiers\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Identifiers.GetUserAsync(userKey);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022add-identifier-to-user\u0022\u003EAdd Identifier to User\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Identifiers.AddUserAsync(userKey, request);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022update-identifier-1\u0022\u003EUpdate Identifier\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Identifiers.UpdateUserAsync(userKey, request);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022delete-identifier-1\u0022\u003EDelete Identifier\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Identifiers.DeleteUserAsync(userKey, request);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022state-events\u0022\u003E\uD83D\uDD04 State Events\u003C/h2\u003E\n\u003Cp\u003EIdentifier changes trigger events:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EIdentifiersChanged\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Useful for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUI updates\u003C/li\u003E\n\u003Cli\u003ECache invalidation\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-considerations\u0022\u003E\uD83D\uDD10 Security Considerations\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EIdentifiers may require verification\u003C/li\u003E\n\u003Cli\u003ELogin identifiers can be restricted\u003C/li\u003E\n\u003Cli\u003EPrimary identifier can be controlled\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022summary\u0022\u003E\uD83C\uDFAF Summary\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth identifiers:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eare independent entities\u003C/li\u003E\n\u003Cli\u003Esupport multiple types\u003C/li\u003E\n\u003Cli\u003Eseparate login vs non-login identifiers\u003C/li\u003E\n\u003Cli\u003Eare fully manageable via client\u003C/li\u003E\n\u003C/ul\u003E\n", + "Headings": [ + { + "Id": "overview", + "Text": "\uD83E\uDDE0 Overview", + "Level": 0 + }, + { + "Id": "what-is-an-identifier", + "Text": "\uD83D\uDD11 What is an Identifier?", + "Level": 0 + }, + { + "Id": "primary-identifier", + "Text": "\u2B50 Primary Identifier", + "Level": 0 + }, + { + "Id": "login-identifiers", + "Text": "\uD83D\uDD10 Login Identifiers", + "Level": 0 + }, + { + "Id": "get-my-identifiers", + "Text": "\uD83D\uDCCB Get My Identifiers", + "Level": 0 + }, + { + "Id": "add-identifier", + "Text": "\u2795 Add Identifier", + "Level": 0 + }, + { + "Id": "update-identifier", + "Text": "\u270F\uFE0F Update Identifier", + "Level": 0 + }, + { + "Id": "verify-identifier", + "Text": "\u2705 Verify Identifier", + "Level": 0 + }, + { + "Id": "delete-identifier", + "Text": "\u274C Delete Identifier", + "Level": 0 + }, + { + "Id": "admin-operations", + "Text": "\uD83D\uDC64 Admin Operations", + "Level": 0 + }, + { + "Id": "get-user-identifiers", + "Text": "Get User Identifiers", + "Level": 1 + }, + { + "Id": "add-identifier-to-user", + "Text": "Add Identifier to User", + "Level": 1 + }, + { + "Id": "update-identifier-1", + "Text": "Update Identifier", + "Level": 1 + }, + { + "Id": "delete-identifier-1", + "Text": "Delete Identifier", + "Level": 1 + }, + { + "Id": "state-events", + "Text": "\uD83D\uDD04 State Events", + "Level": 0 + }, + { + "Id": "security-considerations", + "Text": "\uD83D\uDD10 Security Considerations", + "Level": 0 + }, + { + "Id": "summary", + "Text": "\uD83C\uDFAF Summary", + "Level": 0 + } + ] } \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/index.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/index.json index 6d4ae5d6..bcd0a44e 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/index.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/index.json @@ -1,5 +1,47 @@ { "Slug": "client/index", "Title": "Client \u0026 API", - "Html": "\n\u003Cp\u003EUltimateAuth Client is a \u003Cstrong\u003Ehigh-level SDK\u003C/strong\u003E designed to simplify authentication flows.\u003C/p\u003E\n\u003Cp\u003EIt is NOT just an HTTP wrapper.\u003C/p\u003E\n\u003Ch2 id=\u0022what-makes-the-client-different\u0022\u003E\uD83E\uDDE0 What Makes the Client Different?\u003C/h2\u003E\n\u003Cp\u003EThe client:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EHandles full authentication flows (login, PKCE, refresh)\u003C/li\u003E\n\u003Cli\u003EManages redirects automatically\u003C/li\u003E\n\u003Cli\u003EPublishes state events\u003C/li\u003E\n\u003Cli\u003EProvides structured results\u003C/li\u003E\n\u003Cli\u003EWorks across multiple client types (SPA, server, hybrid)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You SHOULD use the client instead of calling endpoints manually.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022client-architecture\u0022\u003E\uD83E\uDDF1 Client Architecture\u003C/h2\u003E\n\u003Cp\u003EThe client is split into multiple specialized services:\u003C/p\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EService\u003C/th\u003E\n\u003Cth\u003EResponsibility\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EFlowClient\u003C/td\u003E\n\u003Ctd\u003ELogin, logout, refresh, PKCE\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ESessionClient\u003C/td\u003E\n\u003Ctd\u003ESession \u0026amp; device management\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EUserClient\u003C/td\u003E\n\u003Ctd\u003EUser profile \u0026amp; lifecycle\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EIdentifierClient\u003C/td\u003E\n\u003Ctd\u003EEmail / username / phone management\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ECredentialClient\u003C/td\u003E\n\u003Ctd\u003EPassword management\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EAuthorizationClient\u003C/td\u003E\n\u003Ctd\u003ERoles \u0026amp; permissions\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022client-entry-point\u0022\u003E\uD83E\uDDE9 Client Entry Point\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth exposes a single entry point:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ccode\u003EUAuthClient\u003C/code\u003E\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003E[Inject] IUAuthClient UAuthClient { get; set; } = null!;\n\nawait UAuthClient.Flows.LoginAsync(...);\nawait UAuthClient.Users.GetMeAsync();\nawait UAuthClient.Sessions.GetMyChainsAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022core-concept-flow-based-design\u0022\u003E\uD83D\uDD11 Core Concept: Flow-Based Design\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth is \u003Cstrong\u003Eflow-oriented\u003C/strong\u003E, not endpoint-oriented.\u003C/p\u003E\n\u003Cp\u003EInstead of calling endpoints:\u003C/p\u003E\n\u003Cp\u003E\u274C POST /auth/login\u003Cbr /\u003E\n\u2714 flowClient.LoginAsync()\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022example\u0022\u003E\u26A1 Example\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Flows.LoginAsync(new LoginRequest\n{\n Identifier = \u0026quot;user@ultimateauth.com\u0026quot;,\n Secret = \u0026quot;password\u0026quot;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This automatically:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EBuilds request payload\u003C/li\u003E\n\u003Cli\u003EHandles redirect\u003C/li\u003E\n\u003Cli\u003EIntegrates with configured endpoints\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022state-events\u0022\u003E\uD83D\uDD04 State Events\u003C/h2\u003E\n\u003Cp\u003EClient automatically publishes events:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESessionRevoked\u003C/li\u003E\n\u003Cli\u003EProfileChanged\u003C/li\u003E\n\u003Cli\u003EAuthorizationChanged\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022how-to-use-this-section\u0022\u003E\uD83E\uDDED How to Use This Section\u003C/h2\u003E\n\u003Cp\u003EFollow these guides:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eauthentication.md \u2192 login, refresh, logout\u003C/li\u003E\n\u003Cli\u003Esession-management.md \u2192 sessions \u0026amp; devices\u003C/li\u003E\n\u003Cli\u003Euser-management.md \u2192 user operations\u003C/li\u003E\n\u003Cli\u003Eidentifiers.md \u2192 login identifiers\u003C/li\u003E\n\u003Cli\u003Eauthorization.md \u2192 roles \u0026amp; permissions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022summary\u0022\u003E\uD83C\uDFAF Summary\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth Client:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eabstracts complexity\u003C/li\u003E\n\u003Cli\u003Eenforces correct flows\u003C/li\u003E\n\u003Cli\u003Ereduces security mistakes\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Think of it as:\u003C/p\u003E\n\u003Cp\u003E\u003Cstrong\u003E\u201CAuthentication runtime for your frontend / app\u201D\u003C/strong\u003E\u003C/p\u003E\n" + "Html": "\n\u003Cp\u003EUltimateAuth Client is a \u003Cstrong\u003Ehigh-level SDK\u003C/strong\u003E designed to simplify authentication flows.\u003C/p\u003E\n\u003Cp\u003EIt is NOT just an HTTP wrapper.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-makes-the-client-different\u0022\u003E\uD83E\uDDE0 What Makes the Client Different?\u003C/h2\u003E\n\u003Cp\u003EThe client:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EHandles full authentication flows (login, PKCE, refresh)\u003C/li\u003E\n\u003Cli\u003EManages redirects automatically\u003C/li\u003E\n\u003Cli\u003EPublishes state events\u003C/li\u003E\n\u003Cli\u003EProvides structured results\u003C/li\u003E\n\u003Cli\u003EWorks across multiple client types (SPA, server, hybrid)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You SHOULD use the client instead of calling endpoints manually.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022client-architecture\u0022\u003E\uD83E\uDDF1 Client Architecture\u003C/h2\u003E\n\u003Cp\u003EThe client is split into multiple specialized services:\u003C/p\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EService\u003C/th\u003E\n\u003Cth\u003EResponsibility\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EFlowClient\u003C/td\u003E\n\u003Ctd\u003ELogin, logout, refresh, PKCE\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ESessionClient\u003C/td\u003E\n\u003Ctd\u003ESession \u0026amp; device management\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EUserClient\u003C/td\u003E\n\u003Ctd\u003EUser profile \u0026amp; lifecycle\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EIdentifierClient\u003C/td\u003E\n\u003Ctd\u003EEmail / username / phone management\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ECredentialClient\u003C/td\u003E\n\u003Ctd\u003EPassword management\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EAuthorizationClient\u003C/td\u003E\n\u003Ctd\u003ERoles \u0026amp; permissions\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022client-entry-point\u0022\u003E\uD83E\uDDE9 Client Entry Point\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth exposes a single entry point:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ccode\u003EUAuthClient\u003C/code\u003E\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003E[Inject] IUAuthClient UAuthClient { get; set; } = null!;\n\nawait UAuthClient.Flows.LoginAsync(...);\nawait UAuthClient.Users.GetMeAsync();\nawait UAuthClient.Sessions.GetMyChainsAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022core-concept-flow-based-design\u0022\u003E\uD83D\uDD11 Core Concept: Flow-Based Design\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth is \u003Cstrong\u003Eflow-oriented\u003C/strong\u003E, not endpoint-oriented.\u003C/p\u003E\n\u003Cp\u003EInstead of calling endpoints:\u003C/p\u003E\n\u003Cp\u003E\u274C POST /auth/login\u003Cbr /\u003E\n\u2714 flowClient.LoginAsync()\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022example\u0022\u003E\u26A1 Example\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Flows.LoginAsync(new LoginRequest\n{\n Identifier = \u0026quot;user@ultimateauth.com\u0026quot;,\n Secret = \u0026quot;password\u0026quot;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This automatically:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EBuilds request payload\u003C/li\u003E\n\u003Cli\u003EHandles redirect\u003C/li\u003E\n\u003Cli\u003EIntegrates with configured endpoints\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022state-events\u0022\u003E\uD83D\uDD04 State Events\u003C/h2\u003E\n\u003Cp\u003EClient automatically publishes events:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESessionRevoked\u003C/li\u003E\n\u003Cli\u003EProfileChanged\u003C/li\u003E\n\u003Cli\u003EAuthorizationChanged\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022how-to-use-this-section\u0022\u003E\uD83E\uDDED How to Use This Section\u003C/h2\u003E\n\u003Cp\u003EFollow these guides:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eauthentication.md \u2192 login, refresh, logout\u003C/li\u003E\n\u003Cli\u003Esession-management.md \u2192 sessions \u0026amp; devices\u003C/li\u003E\n\u003Cli\u003Euser-management.md \u2192 user operations\u003C/li\u003E\n\u003Cli\u003Eidentifiers.md \u2192 login identifiers\u003C/li\u003E\n\u003Cli\u003Eauthorization.md \u2192 roles \u0026amp; permissions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022summary\u0022\u003E\uD83C\uDFAF Summary\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth Client:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eabstracts complexity\u003C/li\u003E\n\u003Cli\u003Eenforces correct flows\u003C/li\u003E\n\u003Cli\u003Ereduces security mistakes\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Think of it as:\u003C/p\u003E\n\u003Cp\u003E\u003Cstrong\u003E\u201CAuthentication runtime for your frontend / app\u201D\u003C/strong\u003E\u003C/p\u003E\n", + "Headings": [ + { + "Id": "what-makes-the-client-different", + "Text": "\uD83E\uDDE0 What Makes the Client Different?", + "Level": 0 + }, + { + "Id": "client-architecture", + "Text": "\uD83E\uDDF1 Client Architecture", + "Level": 0 + }, + { + "Id": "client-entry-point", + "Text": "\uD83E\uDDE9 Client Entry Point", + "Level": 0 + }, + { + "Id": "core-concept-flow-based-design", + "Text": "\uD83D\uDD11 Core Concept: Flow-Based Design", + "Level": 0 + }, + { + "Id": "example", + "Text": "\u26A1 Example", + "Level": 0 + }, + { + "Id": "state-events", + "Text": "\uD83D\uDD04 State Events", + "Level": 0 + }, + { + "Id": "how-to-use-this-section", + "Text": "\uD83E\uDDED How to Use This Section", + "Level": 0 + }, + { + "Id": "summary", + "Text": "\uD83C\uDFAF Summary", + "Level": 0 + } + ] } \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/session-management.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/session-management.json index 2cf70109..b6189d48 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/session-management.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/session-management.json @@ -1,5 +1,87 @@ { "Slug": "client/session-management", "Title": "Session Management", - "Html": "\n\u003Cp\u003EThis section explains how to manage sessions and devices using the UltimateAuth client.\u003C/p\u003E\n\u003Ch2 id=\u0022overview\u0022\u003E\uD83E\uDDE0 Overview\u003C/h2\u003E\n\u003Cp\u003EIn UltimateAuth, sessions are \u003Cstrong\u003Enot just tokens\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EThey are structured as:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERoot \u2192 user-level security\u003C/li\u003E\n\u003Cli\u003EChain \u2192 device (browser / mobile / app)\u003C/li\u003E\n\u003Cli\u003ESession \u2192 individual authentication instance\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 On the client, you interact with:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003E[Inject] IUAuthClient UAuthClient { get; set; } = null!;\n\nawait UAuthClient.Sessions...\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022get-active-sessions-devices\u0022\u003E\uD83D\uDCCB Get Active Sessions (Devices)\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Evar result = await UAuthClient.Sessions.GetMyChainsAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Returns:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EActive devices\u003C/li\u003E\n\u003Cli\u003ESession chains\u003C/li\u003E\n\u003Cli\u003EMetadata (device, timestamps)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022get-session-detail\u0022\u003E\uD83D\uDD0D Get Session Detail\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Evar detail = await UAuthClient.Sessions.GetMyChainDetailAsync(chainId);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Use this to:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EInspect a specific device\u003C/li\u003E\n\u003Cli\u003EView session history\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022logout-vs-revoke-important\u0022\u003E\uD83D\uDEAA Logout vs Revoke (Important)\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth distinguishes between:\u003C/p\u003E\n\u003Ch3 id=\u0022logout\u0022\u003ELogout\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Flows.LogoutAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cul\u003E\n\u003Cli\u003EEnds \u003Cstrong\u003Ecurrent session\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EUser can login again normally\u003C/li\u003E\n\u003Cli\u003EDoes NOT affect other devices\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022revoke-session-control\u0022\u003ERevoke (Session Control)\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Sessions.RevokeMyChainAsync(chainId);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cul\u003E\n\u003Cli\u003EInvalidates entire \u003Cstrong\u003Edevice chain\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EAll sessions under that device are revoked\u003C/li\u003E\n\u003Cli\u003ECannot be restored\u003C/li\u003E\n\u003Cli\u003ENew login creates a new chain\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Key difference:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogout = end current session\u003C/li\u003E\n\u003Cli\u003ERevoke = destroy device identity\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EFor standard auth process, use \u003Ccode\u003EUAuthClient.Flows.LogoutMyDeviceAsync(chainId)\u003C/code\u003E instead of \u003Ccode\u003EUAuthClient.Sessions.RevokeMyChainAsync(chainId)\u003C/code\u003E\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022revoke-other-devices\u0022\u003E\uD83D\uDCF1 Revoke Other Devices\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Sessions.RevokeMyOtherChainsAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EKeeps current device active\u003C/li\u003E\n\u003Cli\u003ELogs out all other devices\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022revoke-all-sessions\u0022\u003E\uD83D\uDCA5 Revoke All Sessions\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Sessions.RevokeAllMyChainsAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogs out ALL devices (including current)\u003C/li\u003E\n\u003Cli\u003EForces full reauthentication everywhere\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022admin-session-management\u0022\u003E\uD83D\uDC64 Admin Session Management\u003C/h2\u003E\n\u003Ch3 id=\u0022get-user-devices\u0022\u003EGet User Devices\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Sessions.GetUserChainsAsync(userKey);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022revoke-specific-session\u0022\u003ERevoke Specific Session\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Sessions.RevokeUserSessionAsync(userKey, sessionId);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022revoke-device-chain\u0022\u003ERevoke Device (Chain)\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Sessions.RevokeUserChainAsync(userKey, chainId);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022revoke-all-user-sessions\u0022\u003ERevoke All User Sessions\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Sessions.RevokeAllUserChainsAsync(userKey);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022device-model\u0022\u003E\uD83E\uDDE0 Device Model\u003C/h2\u003E\n\u003Cp\u003EEach chain represents a \u003Cstrong\u003Edevice identity\u003C/strong\u003E:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EBrowser instance\u003C/li\u003E\n\u003Cli\u003EMobile device\u003C/li\u003E\n\u003Cli\u003EApplication instance\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Sessions are grouped under chains.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022security-implications\u0022\u003E\uD83D\uDD10 Security Implications\u003C/h2\u003E\n\u003Cp\u003ESession operations are security-critical:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERevoke is irreversible\u003C/li\u003E\n\u003Cli\u003EDevice isolation is enforced\u003C/li\u003E\n\u003Cli\u003ECross-device attacks are contained\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022summary\u0022\u003E\uD83C\uDFAF Summary\u003C/h2\u003E\n\u003Cp\u003ESession management in UltimateAuth:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eis device-aware\u003C/li\u003E\n\u003Cli\u003Eseparates logout vs revoke\u003C/li\u003E\n\u003Cli\u003Egives full control over user sessions\u003C/li\u003E\n\u003C/ul\u003E\n" + "Html": "\n\u003Cp\u003EThis section explains how to manage sessions and devices using the UltimateAuth client.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022overview\u0022\u003E\uD83E\uDDE0 Overview\u003C/h2\u003E\n\u003Cp\u003EIn UltimateAuth, sessions are \u003Cstrong\u003Enot just tokens\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EThey are structured as:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERoot \u2192 user-level security\u003C/li\u003E\n\u003Cli\u003EChain \u2192 device (browser / mobile / app)\u003C/li\u003E\n\u003Cli\u003ESession \u2192 individual authentication instance\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 On the client, you interact with:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003E[Inject] IUAuthClient UAuthClient { get; set; } = null!;\n\nawait UAuthClient.Sessions...\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022get-active-sessions-devices\u0022\u003E\uD83D\uDCCB Get Active Sessions (Devices)\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Evar result = await UAuthClient.Sessions.GetMyChainsAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Returns:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EActive devices\u003C/li\u003E\n\u003Cli\u003ESession chains\u003C/li\u003E\n\u003Cli\u003EMetadata (device, timestamps)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022get-session-detail\u0022\u003E\uD83D\uDD0D Get Session Detail\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Evar detail = await UAuthClient.Sessions.GetMyChainDetailAsync(chainId);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Use this to:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EInspect a specific device\u003C/li\u003E\n\u003Cli\u003EView session history\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022logout-vs-revoke-important\u0022\u003E\uD83D\uDEAA Logout vs Revoke (Important)\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth distinguishes between:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022logout\u0022\u003ELogout\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Flows.LogoutAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cul\u003E\n\u003Cli\u003EEnds \u003Cstrong\u003Ecurrent session\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EUser can login again normally\u003C/li\u003E\n\u003Cli\u003EDoes NOT affect other devices\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022revoke-session-control\u0022\u003ERevoke (Session Control)\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Sessions.RevokeMyChainAsync(chainId);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cul\u003E\n\u003Cli\u003EInvalidates entire \u003Cstrong\u003Edevice chain\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EAll sessions under that device are revoked\u003C/li\u003E\n\u003Cli\u003ECannot be restored\u003C/li\u003E\n\u003Cli\u003ENew login creates a new chain\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Key difference:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogout = end current session\u003C/li\u003E\n\u003Cli\u003ERevoke = destroy device identity\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EFor standard auth process, use \u003Ccode\u003EUAuthClient.Flows.LogoutMyDeviceAsync(chainId)\u003C/code\u003E instead of \u003Ccode\u003EUAuthClient.Sessions.RevokeMyChainAsync(chainId)\u003C/code\u003E\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022revoke-other-devices\u0022\u003E\uD83D\uDCF1 Revoke Other Devices\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Sessions.RevokeMyOtherChainsAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EKeeps current device active\u003C/li\u003E\n\u003Cli\u003ELogs out all other devices\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022revoke-all-sessions\u0022\u003E\uD83D\uDCA5 Revoke All Sessions\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Sessions.RevokeAllMyChainsAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogs out ALL devices (including current)\u003C/li\u003E\n\u003Cli\u003EForces full reauthentication everywhere\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022admin-session-management\u0022\u003E\uD83D\uDC64 Admin Session Management\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022get-user-devices\u0022\u003EGet User Devices\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Sessions.GetUserChainsAsync(userKey);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022revoke-specific-session\u0022\u003ERevoke Specific Session\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Sessions.RevokeUserSessionAsync(userKey, sessionId);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022revoke-device-chain\u0022\u003ERevoke Device (Chain)\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Sessions.RevokeUserChainAsync(userKey, chainId);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022revoke-all-user-sessions\u0022\u003ERevoke All User Sessions\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Sessions.RevokeAllUserChainsAsync(userKey);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022device-model\u0022\u003E\uD83E\uDDE0 Device Model\u003C/h2\u003E\n\u003Cp\u003EEach chain represents a \u003Cstrong\u003Edevice identity\u003C/strong\u003E:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EBrowser instance\u003C/li\u003E\n\u003Cli\u003EMobile device\u003C/li\u003E\n\u003Cli\u003EApplication instance\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Sessions are grouped under chains.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-implications\u0022\u003E\uD83D\uDD10 Security Implications\u003C/h2\u003E\n\u003Cp\u003ESession operations are security-critical:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERevoke is irreversible\u003C/li\u003E\n\u003Cli\u003EDevice isolation is enforced\u003C/li\u003E\n\u003Cli\u003ECross-device attacks are contained\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022summary\u0022\u003E\uD83C\uDFAF Summary\u003C/h2\u003E\n\u003Cp\u003ESession management in UltimateAuth:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eis device-aware\u003C/li\u003E\n\u003Cli\u003Eseparates logout vs revoke\u003C/li\u003E\n\u003Cli\u003Egives full control over user sessions\u003C/li\u003E\n\u003C/ul\u003E\n", + "Headings": [ + { + "Id": "overview", + "Text": "\uD83E\uDDE0 Overview", + "Level": 0 + }, + { + "Id": "get-active-sessions-devices", + "Text": "\uD83D\uDCCB Get Active Sessions (Devices)", + "Level": 0 + }, + { + "Id": "get-session-detail", + "Text": "\uD83D\uDD0D Get Session Detail", + "Level": 0 + }, + { + "Id": "logout-vs-revoke-important", + "Text": "\uD83D\uDEAA Logout vs Revoke (Important)", + "Level": 0 + }, + { + "Id": "logout", + "Text": "Logout", + "Level": 1 + }, + { + "Id": "revoke-session-control", + "Text": "Revoke (Session Control)", + "Level": 1 + }, + { + "Id": "revoke-other-devices", + "Text": "\uD83D\uDCF1 Revoke Other Devices", + "Level": 0 + }, + { + "Id": "revoke-all-sessions", + "Text": "\uD83D\uDCA5 Revoke All Sessions", + "Level": 0 + }, + { + "Id": "admin-session-management", + "Text": "\uD83D\uDC64 Admin Session Management", + "Level": 0 + }, + { + "Id": "get-user-devices", + "Text": "Get User Devices", + "Level": 1 + }, + { + "Id": "revoke-specific-session", + "Text": "Revoke Specific Session", + "Level": 1 + }, + { + "Id": "revoke-device-chain", + "Text": "Revoke Device (Chain)", + "Level": 1 + }, + { + "Id": "revoke-all-user-sessions", + "Text": "Revoke All User Sessions", + "Level": 1 + }, + { + "Id": "device-model", + "Text": "\uD83E\uDDE0 Device Model", + "Level": 0 + }, + { + "Id": "security-implications", + "Text": "\uD83D\uDD10 Security Implications", + "Level": 0 + }, + { + "Id": "summary", + "Text": "\uD83C\uDFAF Summary", + "Level": 0 + } + ] } \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/user-management.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/user-management.json index cc177173..55d8be3e 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/user-management.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/client/user-management.json @@ -1,5 +1,87 @@ { "Slug": "client/user-management", "Title": "User Management", - "Html": "\n\u003Cp\u003EThis section explains how to manage users using the UltimateAuth client.\u003C/p\u003E\n\u003Ch2 id=\u0022overview\u0022\u003E\uD83E\uDDE0 Overview\u003C/h2\u003E\n\u003Cp\u003EUser operations are handled via:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003EUAuthClient.Users...\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThis includes:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EProfile management\u003C/li\u003E\n\u003Cli\u003EUser lifecycle\u003C/li\u003E\n\u003Cli\u003EAdmin operations\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022get-current-user\u0022\u003E\uD83D\uDE4B Get Current User\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Evar me = await UAuthClient.Users.GetMeAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Returns:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUser profile\u003C/li\u003E\n\u003Cli\u003EStatus\u003C/li\u003E\n\u003Cli\u003EBasic identity data\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022update-profile\u0022\u003E\u270F\uFE0F Update Profile\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Users.UpdateMeAsync(new UpdateProfileRequest\n{\n DisplayName = \u0026quot;John Doe\u0026quot;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Triggers:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EProfile update\u003C/li\u003E\n\u003Cli\u003EState event (ProfileChanged)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022delete-current-user\u0022\u003E\u274C Delete Current User\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Users.DeleteMeAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EDeletes user (based on configured mode)\u003C/li\u003E\n\u003Cli\u003EEnds session\u003C/li\u003E\n\u003Cli\u003ETriggers state update\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022admin-query-users\u0022\u003E\uD83D\uDC51 Admin: Query Users\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Evar result = await UAuthClient.Users.QueryAsync(new UserQuery\n{\n Search = \u0026quot;john\u0026quot;,\n PageNumber = 1,\n PageSize = 10\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Supports:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Esearch\u003C/li\u003E\n\u003Cli\u003Epagination\u003C/li\u003E\n\u003Cli\u003Efiltering (status, etc.)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022create-user\u0022\u003E\u2795 Create User\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Users.CreateAsync(new CreateUserRequest\n{\n UserName = \u0026quot;john\u0026quot;,\n Email = \u0026quot;john@mail.com\u0026quot;,\n Password = \u0026quot;123456\u0026quot;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022admin-create\u0022\u003E\uD83D\uDEE0 Admin Create\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Users.CreateAsAdminAsync(request);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022change-status\u0022\u003E\uD83D\uDD04 Change Status\u003C/h2\u003E\n\u003Ch3 id=\u0022self\u0022\u003ESelf\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Users.ChangeMyStatusAsync(new ChangeUserStatusSelfRequest\n{\n Status = UserStatus.SelfSuspended\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022admin\u0022\u003EAdmin\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Users.ChangeUserStatusAsync(userKey, new ChangeUserStatusAdminRequest\n{\n Status = UserStatus.Suspended\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022delete-user-admin\u0022\u003E\u274C Delete User (Admin)\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Users.DeleteUserAsync(userKey, new DeleteUserRequest\n{\n Mode = DeleteMode.Soft\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022get-user\u0022\u003E\uD83D\uDD0D Get User\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Evar user = await UAuthClient.Users.GetUserAsync(userKey);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022update-user-admin\u0022\u003E\u270F\uFE0F Update User (Admin)\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Users.UpdateUserAsync(userKey, request);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022lifecycle-model\u0022\u003E\uD83E\uDDE0 Lifecycle Model\u003C/h2\u003E\n\u003Cp\u003EUsers have a lifecycle:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EActive\u003C/li\u003E\n\u003Cli\u003ESuspended\u003C/li\u003E\n\u003Cli\u003EDisabled\u003C/li\u003E\n\u003Cli\u003EDeleted (soft/hard)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Status impacts:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Elogin ability\u003C/li\u003E\n\u003Cli\u003Esession validity\u003C/li\u003E\n\u003Cli\u003Eauthorization\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022security-notes\u0022\u003E\uD83D\uDD10 Security Notes\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EStatus changes may invalidate sessions\u003C/li\u003E\n\u003Cli\u003EDelete may trigger cleanup across domains\u003C/li\u003E\n\u003Cli\u003EAdmin actions are policy-protected\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022summary\u0022\u003E\uD83C\uDFAF Summary\u003C/h2\u003E\n\u003Cp\u003EUser management in UltimateAuth:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eis lifecycle-aware\u003C/li\u003E\n\u003Cli\u003Esupports self \u002B admin flows\u003C/li\u003E\n\u003Cli\u003Eintegrates with session \u0026amp; security model\u003C/li\u003E\n\u003C/ul\u003E\n" + "Html": "\n\u003Cp\u003EThis section explains how to manage users using the UltimateAuth client.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022overview\u0022\u003E\uD83E\uDDE0 Overview\u003C/h2\u003E\n\u003Cp\u003EUser operations are handled via:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003EUAuthClient.Users...\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThis includes:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EProfile management\u003C/li\u003E\n\u003Cli\u003EUser lifecycle\u003C/li\u003E\n\u003Cli\u003EAdmin operations\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022get-current-user\u0022\u003E\uD83D\uDE4B Get Current User\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Evar me = await UAuthClient.Users.GetMeAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Returns:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUser profile\u003C/li\u003E\n\u003Cli\u003EStatus\u003C/li\u003E\n\u003Cli\u003EBasic identity data\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022update-profile\u0022\u003E\u270F\uFE0F Update Profile\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Users.UpdateMeAsync(new UpdateProfileRequest\n{\n DisplayName = \u0026quot;John Doe\u0026quot;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Triggers:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EProfile update\u003C/li\u003E\n\u003Cli\u003EState event (ProfileChanged)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022delete-current-user\u0022\u003E\u274C Delete Current User\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Users.DeleteMeAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EDeletes user (based on configured mode)\u003C/li\u003E\n\u003Cli\u003EEnds session\u003C/li\u003E\n\u003Cli\u003ETriggers state update\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022admin-query-users\u0022\u003E\uD83D\uDC51 Admin: Query Users\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Evar result = await UAuthClient.Users.QueryAsync(new UserQuery\n{\n Search = \u0026quot;john\u0026quot;,\n PageNumber = 1,\n PageSize = 10\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Supports:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Esearch\u003C/li\u003E\n\u003Cli\u003Epagination\u003C/li\u003E\n\u003Cli\u003Efiltering (status, etc.)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022create-user\u0022\u003E\u2795 Create User\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Users.CreateAsync(new CreateUserRequest\n{\n UserName = \u0026quot;john\u0026quot;,\n Email = \u0026quot;john@mail.com\u0026quot;,\n Password = \u0026quot;123456\u0026quot;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022admin-create\u0022\u003E\uD83D\uDEE0 Admin Create\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Users.CreateAsAdminAsync(request);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022change-status\u0022\u003E\uD83D\uDD04 Change Status\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022self\u0022\u003ESelf\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Users.ChangeMyStatusAsync(new ChangeUserStatusSelfRequest\n{\n Status = UserStatus.SelfSuspended\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022admin\u0022\u003EAdmin\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Users.ChangeUserStatusAsync(userKey, new ChangeUserStatusAdminRequest\n{\n Status = UserStatus.Suspended\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022delete-user-admin\u0022\u003E\u274C Delete User (Admin)\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Users.DeleteUserAsync(userKey, new DeleteUserRequest\n{\n Mode = DeleteMode.Soft\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022get-user\u0022\u003E\uD83D\uDD0D Get User\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Evar user = await UAuthClient.Users.GetUserAsync(userKey);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022update-user-admin\u0022\u003E\u270F\uFE0F Update User (Admin)\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Users.UpdateUserAsync(userKey, request);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022lifecycle-model\u0022\u003E\uD83E\uDDE0 Lifecycle Model\u003C/h2\u003E\n\u003Cp\u003EUsers have a lifecycle:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EActive\u003C/li\u003E\n\u003Cli\u003ESuspended\u003C/li\u003E\n\u003Cli\u003EDisabled\u003C/li\u003E\n\u003Cli\u003EDeleted (soft/hard)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Status impacts:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Elogin ability\u003C/li\u003E\n\u003Cli\u003Esession validity\u003C/li\u003E\n\u003Cli\u003Eauthorization\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-notes\u0022\u003E\uD83D\uDD10 Security Notes\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EStatus changes may invalidate sessions\u003C/li\u003E\n\u003Cli\u003EDelete may trigger cleanup across domains\u003C/li\u003E\n\u003Cli\u003EAdmin actions are policy-protected\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022summary\u0022\u003E\uD83C\uDFAF Summary\u003C/h2\u003E\n\u003Cp\u003EUser management in UltimateAuth:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eis lifecycle-aware\u003C/li\u003E\n\u003Cli\u003Esupports self \u002B admin flows\u003C/li\u003E\n\u003Cli\u003Eintegrates with session \u0026amp; security model\u003C/li\u003E\n\u003C/ul\u003E\n", + "Headings": [ + { + "Id": "overview", + "Text": "\uD83E\uDDE0 Overview", + "Level": 0 + }, + { + "Id": "get-current-user", + "Text": "\uD83D\uDE4B Get Current User", + "Level": 0 + }, + { + "Id": "update-profile", + "Text": "\u270F\uFE0F Update Profile", + "Level": 0 + }, + { + "Id": "delete-current-user", + "Text": "\u274C Delete Current User", + "Level": 0 + }, + { + "Id": "admin-query-users", + "Text": "\uD83D\uDC51 Admin: Query Users", + "Level": 0 + }, + { + "Id": "create-user", + "Text": "\u2795 Create User", + "Level": 0 + }, + { + "Id": "admin-create", + "Text": "\uD83D\uDEE0 Admin Create", + "Level": 0 + }, + { + "Id": "change-status", + "Text": "\uD83D\uDD04 Change Status", + "Level": 0 + }, + { + "Id": "self", + "Text": "Self", + "Level": 1 + }, + { + "Id": "admin", + "Text": "Admin", + "Level": 1 + }, + { + "Id": "delete-user-admin", + "Text": "\u274C Delete User (Admin)", + "Level": 0 + }, + { + "Id": "get-user", + "Text": "\uD83D\uDD0D Get User", + "Level": 0 + }, + { + "Id": "update-user-admin", + "Text": "\u270F\uFE0F Update User (Admin)", + "Level": 0 + }, + { + "Id": "lifecycle-model", + "Text": "\uD83E\uDDE0 Lifecycle Model", + "Level": 0 + }, + { + "Id": "security-notes", + "Text": "\uD83D\uDD10 Security Notes", + "Level": 0 + }, + { + "Id": "summary", + "Text": "\uD83C\uDFAF Summary", + "Level": 0 + } + ] } \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/advanced-configuration.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/advanced-configuration.json index 289ac798..051d3cbc 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/advanced-configuration.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/advanced-configuration.json @@ -1,5 +1,72 @@ { "Slug": "configuration/advanced-configuration", "Title": "Advanced Configuration", - "Html": "\n\u003Cp\u003EUltimateAuth is designed to be flexible \u2014 but not fragile.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 You can customize almost every part of the system\u003Cbr /\u003E\n\uD83D\uDC49 Without breaking its security guarantees\u003C/p\u003E\n\u003Ch2 id=\u0022philosophy\u0022\u003E\u26A0\uFE0F Philosophy\u003C/h2\u003E\n\u003Cp\u003ECustomization in UltimateAuth follows one rule:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 You can extend behavior\u003Cbr /\u003E\n\uD83D\uDC49 You should not bypass security\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022extension-points\u0022\u003E\uD83E\uDDE9 Extension Points\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth exposes multiple extension points:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EResolvers\u003C/li\u003E\n\u003Cli\u003EValidators\u003C/li\u003E\n\u003Cli\u003EAuthorities\u003C/li\u003E\n\u003Cli\u003EOrchestrators\u003C/li\u003E\n\u003Cli\u003EStores\u003C/li\u003E\n\u003Cli\u003EEvents\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You don\u2019t replace the system\u003Cbr /\u003E\n\uD83D\uDC49 You plug into it\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022replacing-services\u0022\u003E\uD83D\uDD0C Replacing Services\u003C/h2\u003E\n\u003Cp\u003EAll core services can be overridden using DI:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eservices.AddScoped\u0026lt;ISessionValidator, CustomSessionValidator\u0026gt;();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This allows deep customization\u003Cbr /\u003E\n\uD83D\uDC49 While preserving the pipeline\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022authorities-decisions\u0022\u003E\uD83E\uDDE0 Authorities \u0026amp; Decisions\u003C/h2\u003E\n\u003Cp\u003EAuthorities are responsible for decisions:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELoginAuthority\u003C/li\u003E\n\u003Cli\u003EAccessAuthority\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Cp\u003EYou can override them:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eservices.AddScoped\u0026lt;ILoginAuthority, CustomLoginAuthority\u0026gt;();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This changes decision logic\u003Cbr /\u003E\n\uD83D\uDC49 Without touching flows\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022orchestrators\u0022\u003E\uD83D\uDD04 Orchestrators\u003C/h2\u003E\n\u003Cp\u003EOrchestrators coordinate execution:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EValidate\u003C/li\u003E\n\u003Cli\u003EAuthorize\u003C/li\u003E\n\u003Cli\u003EExecute command\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 They enforce invariants\u003Cbr /\u003E\n\uD83D\uDC49 Do not bypass them, unless you exact know what you are doing\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022store-customization\u0022\u003E\uD83D\uDDC4 Store Customization\u003C/h2\u003E\n\u003Cp\u003EYou can provide custom stores:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession store\u003C/li\u003E\n\u003Cli\u003ERefresh token store\u003C/li\u003E\n\u003Cli\u003EUser store\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Supports EF Core, in-memory, or custom implementations\u003C/p\u003E\n\u003Ch2 id=\u0022events\u0022\u003E\uD83D\uDCE1 Events\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth provides event hooks:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogin\u003C/li\u003E\n\u003Cli\u003ELogout\u003C/li\u003E\n\u003Cli\u003ERefresh\u003C/li\u003E\n\u003Cli\u003ERevoke\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.Events.OnUserLoggedIn = ctx =\u0026gt;\n{\n // custom logic\n return Task.CompletedTask;\n};\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Use events for side-effects\u003Cbr /\u003E\n\uD83D\uDC49 Not for core logic\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mode-configuration-overrides\u0022\u003E\u2699\uFE0F Mode Configuration Overrides\u003C/h2\u003E\n\u003Cp\u003EYou can customize behavior per mode:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.ModeConfigurations[UAuthMode.Hybrid] = options =\u0026gt;\n{\n options.Token.AccessTokenLifetime = TimeSpan.FromMinutes(5);\n};\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Runs after defaults\u003Cbr /\u003E\n\uD83D\uDC49 Allows fine-grained control\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022custom-resolvers\u0022\u003E\uD83D\uDD10 Custom Resolvers\u003C/h2\u003E\n\u003Cp\u003EYou can override how data is resolved:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ETenant resolver\u003C/li\u003E\n\u003Cli\u003ESession resolver\u003C/li\u003E\n\u003Cli\u003EDevice resolver\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Enables full control over request interpretation\u003C/p\u003E\n\u003Ch2 id=\u0022safety-boundaries\u0022\u003E\uD83D\uDEE1 Safety Boundaries\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth enforces:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EInvariants\u003C/li\u003E\n\u003Cli\u003EValidation\u003C/li\u003E\n\u003Cli\u003EFail-fast behavior\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Unsafe overrides will fail early\u003C/p\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Extend the system\u003Cbr /\u003E\n\uD83D\uDC49 Don\u2019t fight the system\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch2 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EEverything is replaceable via DI\u003C/li\u003E\n\u003Cli\u003EAuthorities control decisions\u003C/li\u003E\n\u003Cli\u003EOrchestrators enforce flow\u003C/li\u003E\n\u003Cli\u003EEvents are for side-effects\u003C/li\u003E\n\u003Cli\u003ESecurity boundaries are protected\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Ch2 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EReturn to \u003Cstrong\u003EAuth Flows\u003C/strong\u003E or explore \u003Cstrong\u003EPlugin Domains\u003C/strong\u003E\u003C/p\u003E\n" + "Html": "\n\u003Cp\u003EUltimateAuth is designed to be flexible \u2014 but not fragile.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 You can customize almost every part of the system\u003Cbr /\u003E\n\uD83D\uDC49 Without breaking its security guarantees\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022philosophy\u0022\u003E\u26A0\uFE0F Philosophy\u003C/h2\u003E\n\u003Cp\u003ECustomization in UltimateAuth follows one rule:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 You can extend behavior\u003Cbr /\u003E\n\uD83D\uDC49 You should not bypass security\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022extension-points\u0022\u003E\uD83E\uDDE9 Extension Points\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth exposes multiple extension points:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EResolvers\u003C/li\u003E\n\u003Cli\u003EValidators\u003C/li\u003E\n\u003Cli\u003EAuthorities\u003C/li\u003E\n\u003Cli\u003EOrchestrators\u003C/li\u003E\n\u003Cli\u003EStores\u003C/li\u003E\n\u003Cli\u003EEvents\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You don\u2019t replace the system\u003Cbr /\u003E\n\uD83D\uDC49 You plug into it\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022replacing-services\u0022\u003E\uD83D\uDD0C Replacing Services\u003C/h2\u003E\n\u003Cp\u003EAll core services can be overridden using DI:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eservices.AddScoped\u0026lt;ISessionValidator, CustomSessionValidator\u0026gt;();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This allows deep customization\u003Cbr /\u003E\n\uD83D\uDC49 While preserving the pipeline\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022authorities-decisions\u0022\u003E\uD83E\uDDE0 Authorities \u0026amp; Decisions\u003C/h2\u003E\n\u003Cp\u003EAuthorities are responsible for decisions:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELoginAuthority\u003C/li\u003E\n\u003Cli\u003EAccessAuthority\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Cp\u003EYou can override them:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eservices.AddScoped\u0026lt;ILoginAuthority, CustomLoginAuthority\u0026gt;();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This changes decision logic\u003Cbr /\u003E\n\uD83D\uDC49 Without touching flows\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022orchestrators\u0022\u003E\uD83D\uDD04 Orchestrators\u003C/h2\u003E\n\u003Cp\u003EOrchestrators coordinate execution:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EValidate\u003C/li\u003E\n\u003Cli\u003EAuthorize\u003C/li\u003E\n\u003Cli\u003EExecute command\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 They enforce invariants\u003Cbr /\u003E\n\uD83D\uDC49 Do not bypass them, unless you exact know what you are doing\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022store-customization\u0022\u003E\uD83D\uDDC4 Store Customization\u003C/h2\u003E\n\u003Cp\u003EYou can provide custom stores:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession store\u003C/li\u003E\n\u003Cli\u003ERefresh token store\u003C/li\u003E\n\u003Cli\u003EUser store\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Supports EF Core, in-memory, or custom implementations\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022events\u0022\u003E\uD83D\uDCE1 Events\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth provides event hooks:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogin\u003C/li\u003E\n\u003Cli\u003ELogout\u003C/li\u003E\n\u003Cli\u003ERefresh\u003C/li\u003E\n\u003Cli\u003ERevoke\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.Events.OnUserLoggedIn = ctx =\u0026gt;\n{\n // custom logic\n return Task.CompletedTask;\n};\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Use events for side-effects\u003Cbr /\u003E\n\uD83D\uDC49 Not for core logic\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mode-configuration-overrides\u0022\u003E\u2699\uFE0F Mode Configuration Overrides\u003C/h2\u003E\n\u003Cp\u003EYou can customize behavior per mode:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.ModeConfigurations[UAuthMode.Hybrid] = options =\u0026gt;\n{\n options.Token.AccessTokenLifetime = TimeSpan.FromMinutes(5);\n};\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Runs after defaults\u003Cbr /\u003E\n\uD83D\uDC49 Allows fine-grained control\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022custom-resolvers\u0022\u003E\uD83D\uDD10 Custom Resolvers\u003C/h2\u003E\n\u003Cp\u003EYou can override how data is resolved:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ETenant resolver\u003C/li\u003E\n\u003Cli\u003ESession resolver\u003C/li\u003E\n\u003Cli\u003EDevice resolver\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Enables full control over request interpretation\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022safety-boundaries\u0022\u003E\uD83D\uDEE1 Safety Boundaries\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth enforces:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EInvariants\u003C/li\u003E\n\u003Cli\u003EValidation\u003C/li\u003E\n\u003Cli\u003EFail-fast behavior\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Unsafe overrides will fail early\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Extend the system\u003Cbr /\u003E\n\uD83D\uDC49 Don\u2019t fight the system\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EEverything is replaceable via DI\u003C/li\u003E\n\u003Cli\u003EAuthorities control decisions\u003C/li\u003E\n\u003Cli\u003EOrchestrators enforce flow\u003C/li\u003E\n\u003Cli\u003EEvents are for side-effects\u003C/li\u003E\n\u003Cli\u003ESecurity boundaries are protected\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EReturn to \u003Cstrong\u003EAuth Flows\u003C/strong\u003E or explore \u003Cstrong\u003EPlugin Domains\u003C/strong\u003E\u003C/p\u003E\n", + "Headings": [ + { + "Id": "philosophy", + "Text": "\u26A0\uFE0F Philosophy", + "Level": 0 + }, + { + "Id": "extension-points", + "Text": "\uD83E\uDDE9 Extension Points", + "Level": 0 + }, + { + "Id": "replacing-services", + "Text": "\uD83D\uDD0C Replacing Services", + "Level": 0 + }, + { + "Id": "authorities-decisions", + "Text": "\uD83E\uDDE0 Authorities \u0026 Decisions", + "Level": 0 + }, + { + "Id": "orchestrators", + "Text": "\uD83D\uDD04 Orchestrators", + "Level": 0 + }, + { + "Id": "store-customization", + "Text": "\uD83D\uDDC4 Store Customization", + "Level": 0 + }, + { + "Id": "events", + "Text": "\uD83D\uDCE1 Events", + "Level": 0 + }, + { + "Id": "mode-configuration-overrides", + "Text": "\u2699\uFE0F Mode Configuration Overrides", + "Level": 0 + }, + { + "Id": "custom-resolvers", + "Text": "\uD83D\uDD10 Custom Resolvers", + "Level": 0 + }, + { + "Id": "safety-boundaries", + "Text": "\uD83D\uDEE1 Safety Boundaries", + "Level": 0 + }, + { + "Id": "mental-model", + "Text": "\uD83E\uDDE0 Mental Model", + "Level": 0 + }, + { + "Id": "key-takeaways", + "Text": "\uD83D\uDCCC Key Takeaways", + "Level": 0 + }, + { + "Id": "next-step", + "Text": "\u27A1\uFE0F Next Step", + "Level": 0 + } + ] } \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/client-options.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/client-options.json index ebfda14d..90b12565 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/client-options.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/client-options.json @@ -1,5 +1,57 @@ { "Slug": "configuration/client-options", "Title": "Client Options", - "Html": "\n\u003Cp\u003EClient Options define how UltimateAuth behaves on the \u003Cstrong\u003Eclient side\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 While Server controls the system,\u003Cbr /\u003E\n\uD83D\uDC49 Client controls how it is \u003Cstrong\u003Eused\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022what-are-client-options\u0022\u003E\uD83E\uDDE0 What Are Client Options?\u003C/h2\u003E\n\u003Cp\u003EClient options configure:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EClient profile (WASM, Server, MAUI, API)\u003C/li\u003E\n\u003Cli\u003EEndpoint communication\u003C/li\u003E\n\u003Cli\u003EPKCE behavior\u003C/li\u003E\n\u003Cli\u003EToken refresh\u003C/li\u003E\n\u003Cli\u003ERe-authentication behavior\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022basic-usage\u0022\u003E\u2699\uFE0F Basic Usage\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthClientBlazor(o =\u0026gt;\n{\n o.AutoDetectClientProfile = false;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022client-profile\u0022\u003E\uD83E\uDDE9 Client Profile\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth automatically detects client type by default:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EBlazor WASM\u003C/li\u003E\n\u003Cli\u003EBlazor Server\u003C/li\u003E\n\u003Cli\u003EMAUI\u003C/li\u003E\n\u003Cli\u003EWebServer\u003C/li\u003E\n\u003Cli\u003EAPI\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EYou can override manually:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.ClientProfile = UAuthClientProfile.BlazorWasm;\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Manual override is useful for testing or special scenarios\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022endpoints\u0022\u003E\uD83C\uDF10 Endpoints\u003C/h2\u003E\n\u003Cp\u003EDefines where requests are sent:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.Endpoints.BasePath = \u0026quot;https://localhost:5001/auth\u0026quot;;\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Required for WASM / remote clients\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022pkce-configuration\u0022\u003E\uD83D\uDD10 PKCE Configuration\u003C/h2\u003E\n\u003Cp\u003EUsed for browser-based login flows:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.Pkce.ReturnUrl = \u0026quot;https://localhost:5002/home\u0026quot;;\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Required for WASM scenarios\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022auto-refresh\u0022\u003E\uD83D\uDD01 Auto Refresh\u003C/h2\u003E\n\u003Cp\u003EControls token/session refresh behavior:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.AutoRefresh.Interval = TimeSpan.FromMinutes(1);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Keeps authentication alive automatically\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022re-authentication\u0022\u003E\uD83D\uDD04 Re-authentication\u003C/h2\u003E\n\u003Cp\u003EControls behavior when session expires:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.Reauth.Behavior = ReauthBehavior.RaiseEvent;\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Allows silent or interactive re-login\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Server decides\u003Cbr /\u003E\n\uD83D\uDC49 Client adapts\u003C/p\u003E\n\u003Ch2 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EClient options control runtime behavior\u003C/li\u003E\n\u003Cli\u003EProfile detection is automatic\u003C/li\u003E\n\u003Cli\u003EPKCE is required for public clients\u003C/li\u003E\n\u003Cli\u003ERefresh and re-auth are configurable\u003C/li\u003E\n\u003Cli\u003EWorks together with Server options\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Ch2 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003EConfiguration Sources\u003C/strong\u003E\u003C/p\u003E\n" + "Html": "\n\u003Cp\u003EClient Options define how UltimateAuth behaves on the \u003Cstrong\u003Eclient side\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 While Server controls the system,\u003Cbr /\u003E\n\uD83D\uDC49 Client controls how it is \u003Cstrong\u003Eused\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-are-client-options\u0022\u003E\uD83E\uDDE0 What Are Client Options?\u003C/h2\u003E\n\u003Cp\u003EClient options configure:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EClient profile (WASM, Server, MAUI, API)\u003C/li\u003E\n\u003Cli\u003EEndpoint communication\u003C/li\u003E\n\u003Cli\u003EPKCE behavior\u003C/li\u003E\n\u003Cli\u003EToken refresh\u003C/li\u003E\n\u003Cli\u003ERe-authentication behavior\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022basic-usage\u0022\u003E\u2699\uFE0F Basic Usage\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthClientBlazor(o =\u0026gt;\n{\n o.AutoDetectClientProfile = false;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022client-profile\u0022\u003E\uD83E\uDDE9 Client Profile\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth automatically detects client type by default:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EBlazor WASM\u003C/li\u003E\n\u003Cli\u003EBlazor Server\u003C/li\u003E\n\u003Cli\u003EMAUI\u003C/li\u003E\n\u003Cli\u003EWebServer\u003C/li\u003E\n\u003Cli\u003EAPI\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EYou can override manually:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.ClientProfile = UAuthClientProfile.BlazorWasm;\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Manual override is useful for testing or special scenarios\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022endpoints\u0022\u003E\uD83C\uDF10 Endpoints\u003C/h2\u003E\n\u003Cp\u003EDefines where requests are sent:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.Endpoints.BasePath = \u0026quot;https://localhost:5001/auth\u0026quot;;\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Required for WASM / remote clients\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022pkce-configuration\u0022\u003E\uD83D\uDD10 PKCE Configuration\u003C/h2\u003E\n\u003Cp\u003EUsed for browser-based login flows:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.Pkce.ReturnUrl = \u0026quot;https://localhost:5002/home\u0026quot;;\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Required for WASM scenarios\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022auto-refresh\u0022\u003E\uD83D\uDD01 Auto Refresh\u003C/h2\u003E\n\u003Cp\u003EControls token/session refresh behavior:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.AutoRefresh.Interval = TimeSpan.FromMinutes(1);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Keeps authentication alive automatically\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022re-authentication\u0022\u003E\uD83D\uDD04 Re-authentication\u003C/h2\u003E\n\u003Cp\u003EControls behavior when session expires:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.Reauth.Behavior = ReauthBehavior.RaiseEvent;\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Allows silent or interactive re-login\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Server decides\u003Cbr /\u003E\n\uD83D\uDC49 Client adapts\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EClient options control runtime behavior\u003C/li\u003E\n\u003Cli\u003EProfile detection is automatic\u003C/li\u003E\n\u003Cli\u003EPKCE is required for public clients\u003C/li\u003E\n\u003Cli\u003ERefresh and re-auth are configurable\u003C/li\u003E\n\u003Cli\u003EWorks together with Server options\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003EConfiguration Sources\u003C/strong\u003E\u003C/p\u003E\n", + "Headings": [ + { + "Id": "what-are-client-options", + "Text": "\uD83E\uDDE0 What Are Client Options?", + "Level": 0 + }, + { + "Id": "basic-usage", + "Text": "\u2699\uFE0F Basic Usage", + "Level": 0 + }, + { + "Id": "client-profile", + "Text": "\uD83E\uDDE9 Client Profile", + "Level": 0 + }, + { + "Id": "endpoints", + "Text": "\uD83C\uDF10 Endpoints", + "Level": 0 + }, + { + "Id": "pkce-configuration", + "Text": "\uD83D\uDD10 PKCE Configuration", + "Level": 0 + }, + { + "Id": "auto-refresh", + "Text": "\uD83D\uDD01 Auto Refresh", + "Level": 0 + }, + { + "Id": "re-authentication", + "Text": "\uD83D\uDD04 Re-authentication", + "Level": 0 + }, + { + "Id": "mental-model", + "Text": "\uD83E\uDDE0 Mental Model", + "Level": 0 + }, + { + "Id": "key-takeaways", + "Text": "\uD83D\uDCCC Key Takeaways", + "Level": 0 + }, + { + "Id": "next-step", + "Text": "\u27A1\uFE0F Next Step", + "Level": 0 + } + ] } \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/configuration-overview.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/configuration-overview.json index e622b782..328f7601 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/configuration-overview.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/configuration-overview.json @@ -1,5 +1,72 @@ { "Slug": "configuration/configuration-overview", "Title": "Configuration Overview", - "Html": "\n\u003Cp\u003EUltimateAuth is not configured as a static system.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 It is configured as a \u003Cstrong\u003Eruntime-adaptive system\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022a-common-misunderstanding\u0022\u003E\u26A0\uFE0F A Common Misunderstanding\u003C/h2\u003E\n\u003Cp\u003EMany frameworks expect you to configure authentication once:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EChoose JWT or cookies\u003C/li\u003E\n\u003Cli\u003ESet token lifetimes\u003C/li\u003E\n\u003Cli\u003EConfigure behavior globally\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 And that configuration applies everywhere\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 UltimateAuth does NOT work like this\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022layered-configuration-model\u0022\u003E\uD83E\uDDE9 Layered Configuration Model\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth separates configuration into distinct layers:\u003C/p\u003E\n\u003Ch3 id=\u0022core-behavior-definition\u0022\u003E\uD83D\uDD39 Core (Behavior Definition)\u003C/h3\u003E\n\u003Cp\u003ECore defines \u003Cstrong\u003Ewhat authentication means\u003C/strong\u003E:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession lifecycle\u003C/li\u003E\n\u003Cli\u003EToken behavior\u003C/li\u003E\n\u003Cli\u003EPKCE rules\u003C/li\u003E\n\u003Cli\u003EMulti-tenant handling\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Core is the foundation\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 But you typically do NOT configure it directly\u003C/p\u003E\n\u003Ch3 id=\u0022server-application-configuration\u0022\u003E\uD83D\uDD39 Server (Application Configuration)\u003C/h3\u003E\n\u003Cp\u003EServer is where you configure the system:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthServer(o =\u0026gt;\n{\n o.Login.MaxFailedAttempts = 5;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThis layer controls:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAllowed authentication modes\u003C/li\u003E\n\u003Cli\u003EEndpoint exposure\u003C/li\u003E\n\u003Cli\u003ECookie behavior\u003C/li\u003E\n\u003Cli\u003ESecurity policies\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This is your main configuration surface\u003C/p\u003E\n\u003Ch3 id=\u0022client-runtime-behavior\u0022\u003E\uD83D\uDD39 Client (Runtime Behavior)\u003C/h3\u003E\n\u003Cp\u003EClient configuration controls:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EClient profile (WASM, Server, MAUI, API)\u003C/li\u003E\n\u003Cli\u003EPKCE behavior\u003C/li\u003E\n\u003Cli\u003EAuto-refresh\u003C/li\u003E\n\u003Cli\u003ERe-authentication\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Client influences how flows are executed\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022runtime-configuration-the-key-idea\u0022\u003E\u26A1 Runtime Configuration (The Key Idea)\u003C/h2\u003E\n\u003Cp\u003EHere is the most important concept:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 UltimateAuth does NOT use configuration as-is\u003C/p\u003E\n\u003Cp\u003EInstead, it computes \u003Cstrong\u003EEffective Configuration per request\u003C/strong\u003E\u003C/p\u003E\n\u003Ch3 id=\u0022how-it-works\u0022\u003E\uD83E\uDDE0 How It Works\u003C/h3\u003E\n\u003Cp\u003EAt runtime:\u003C/p\u003E\n\u003Col\u003E\n\u003Cli\u003EClient profile is detected\u003C/li\u003E\n\u003Cli\u003EFlow type is determined (Login, Refresh, etc.)\u003C/li\u003E\n\u003Cli\u003EAuth mode is resolved\u003C/li\u003E\n\u003Cli\u003EDefaults are applied\u003C/li\u003E\n\u003Cli\u003EMode-specific overrides are applied\u003C/li\u003E\n\u003C/ol\u003E\n\u003Cp\u003E\uD83D\uDC49 This produces:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003EEffectiveOptions\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022from-static-to-dynamic\u0022\u003E\uD83D\uDD04 From Static to Dynamic\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode\u003EUAuthServerOptions (startup)\n \u2193\nMode Resolver\n \u2193\nApply Defaults\n \u2193\nMode Overrides\n \u2193\nEffectiveUAuthServerOptions (runtime)\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Every request can have a different effective configuration\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022why-this-matters\u0022\u003E\uD83C\uDFAF Why This Matters\u003C/h2\u003E\n\u003Cp\u003EThis allows UltimateAuth to:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUse different auth modes per client\u003C/li\u003E\n\u003Cli\u003EAdapt behavior per flow\u003C/li\u003E\n\u003Cli\u003EEnforce security dynamically\u003C/li\u003E\n\u003Cli\u003EAvoid global misconfiguration\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You don\u2019t configure \u201Cone system\u201D\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 You configure a \u003Cstrong\u003Edecision engine\u003C/strong\u003E\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022safety-by-design\u0022\u003E\uD83D\uDEE1 Safety by Design\u003C/h2\u003E\n\u003Cp\u003EEven with dynamic behavior:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EInvalid combinations fail early\u003C/li\u003E\n\u003Cli\u003EDisallowed modes are rejected\u003C/li\u003E\n\u003Cli\u003ESecurity invariants are enforced\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Flexibility does not reduce safety\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022core-vs-effective\u0022\u003E\u2699\uFE0F Core vs Effective\u003C/h2\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EConcept\u003C/th\u003E\n\u003Cth\u003EMeaning\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ECore Options\u003C/td\u003E\n\u003Ctd\u003EBase behavior definitions\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EServer Options\u003C/td\u003E\n\u003Ctd\u003EApplication-level configuration\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EEffective Options\u003C/td\u003E\n\u003Ctd\u003ERuntime-resolved behavior\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Cp\u003E\uD83D\uDC49 Effective options are what actually run\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 You don\u2019t configure authentication\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 You configure how it is \u003Cstrong\u003Eresolved\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EControl runtime \u2192 Server Options\u003C/li\u003E\n\u003Cli\u003EConfigure clients \u2192 Client Options\u003C/li\u003E\n\u003C/ul\u003E\n" + "Html": "\n\u003Cp\u003EUltimateAuth is not configured as a static system.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 It is configured as a \u003Cstrong\u003Eruntime-adaptive system\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022a-common-misunderstanding\u0022\u003E\u26A0\uFE0F A Common Misunderstanding\u003C/h2\u003E\n\u003Cp\u003EMany frameworks expect you to configure authentication once:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EChoose JWT or cookies\u003C/li\u003E\n\u003Cli\u003ESet token lifetimes\u003C/li\u003E\n\u003Cli\u003EConfigure behavior globally\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 And that configuration applies everywhere\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 UltimateAuth does NOT work like this\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022layered-configuration-model\u0022\u003E\uD83E\uDDE9 Layered Configuration Model\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth separates configuration into distinct layers:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022core-behavior-definition\u0022\u003E\uD83D\uDD39 Core (Behavior Definition)\u003C/h3\u003E\n\u003Cp\u003ECore defines \u003Cstrong\u003Ewhat authentication means\u003C/strong\u003E:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession lifecycle\u003C/li\u003E\n\u003Cli\u003EToken behavior\u003C/li\u003E\n\u003Cli\u003EPKCE rules\u003C/li\u003E\n\u003Cli\u003EMulti-tenant handling\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Core is the foundation\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 But you typically do NOT configure it directly\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022server-application-configuration\u0022\u003E\uD83D\uDD39 Server (Application Configuration)\u003C/h3\u003E\n\u003Cp\u003EServer is where you configure the system:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthServer(o =\u0026gt;\n{\n o.Login.MaxFailedAttempts = 5;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThis layer controls:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAllowed authentication modes\u003C/li\u003E\n\u003Cli\u003EEndpoint exposure\u003C/li\u003E\n\u003Cli\u003ECookie behavior\u003C/li\u003E\n\u003Cli\u003ESecurity policies\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This is your main configuration surface\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022client-runtime-behavior\u0022\u003E\uD83D\uDD39 Client (Runtime Behavior)\u003C/h3\u003E\n\u003Cp\u003EClient configuration controls:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EClient profile (WASM, Server, MAUI, API)\u003C/li\u003E\n\u003Cli\u003EPKCE behavior\u003C/li\u003E\n\u003Cli\u003EAuto-refresh\u003C/li\u003E\n\u003Cli\u003ERe-authentication\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Client influences how flows are executed\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022runtime-configuration-the-key-idea\u0022\u003E\u26A1 Runtime Configuration (The Key Idea)\u003C/h2\u003E\n\u003Cp\u003EHere is the most important concept:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 UltimateAuth does NOT use configuration as-is\u003C/p\u003E\n\u003Cp\u003EInstead, it computes \u003Cstrong\u003EEffective Configuration per request\u003C/strong\u003E\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022how-it-works\u0022\u003E\uD83E\uDDE0 How It Works\u003C/h3\u003E\n\u003Cp\u003EAt runtime:\u003C/p\u003E\n\u003Col\u003E\n\u003Cli\u003EClient profile is detected\u003C/li\u003E\n\u003Cli\u003EFlow type is determined (Login, Refresh, etc.)\u003C/li\u003E\n\u003Cli\u003EAuth mode is resolved\u003C/li\u003E\n\u003Cli\u003EDefaults are applied\u003C/li\u003E\n\u003Cli\u003EMode-specific overrides are applied\u003C/li\u003E\n\u003C/ol\u003E\n\u003Cp\u003E\uD83D\uDC49 This produces:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003EEffectiveOptions\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022from-static-to-dynamic\u0022\u003E\uD83D\uDD04 From Static to Dynamic\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode\u003EUAuthServerOptions (startup)\n \u2193\nMode Resolver\n \u2193\nApply Defaults\n \u2193\nMode Overrides\n \u2193\nEffectiveUAuthServerOptions (runtime)\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Every request can have a different effective configuration\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022why-this-matters\u0022\u003E\uD83C\uDFAF Why This Matters\u003C/h2\u003E\n\u003Cp\u003EThis allows UltimateAuth to:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUse different auth modes per client\u003C/li\u003E\n\u003Cli\u003EAdapt behavior per flow\u003C/li\u003E\n\u003Cli\u003EEnforce security dynamically\u003C/li\u003E\n\u003Cli\u003EAvoid global misconfiguration\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You don\u2019t configure \u201Cone system\u201D\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 You configure a \u003Cstrong\u003Edecision engine\u003C/strong\u003E\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022safety-by-design\u0022\u003E\uD83D\uDEE1 Safety by Design\u003C/h2\u003E\n\u003Cp\u003EEven with dynamic behavior:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EInvalid combinations fail early\u003C/li\u003E\n\u003Cli\u003EDisallowed modes are rejected\u003C/li\u003E\n\u003Cli\u003ESecurity invariants are enforced\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Flexibility does not reduce safety\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022core-vs-effective\u0022\u003E\u2699\uFE0F Core vs Effective\u003C/h2\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EConcept\u003C/th\u003E\n\u003Cth\u003EMeaning\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ECore Options\u003C/td\u003E\n\u003Ctd\u003EBase behavior definitions\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EServer Options\u003C/td\u003E\n\u003Ctd\u003EApplication-level configuration\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EEffective Options\u003C/td\u003E\n\u003Ctd\u003ERuntime-resolved behavior\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Cp\u003E\uD83D\uDC49 Effective options are what actually run\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 You don\u2019t configure authentication\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 You configure how it is \u003Cstrong\u003Eresolved\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EControl runtime \u2192 Server Options\u003C/li\u003E\n\u003Cli\u003EConfigure clients \u2192 Client Options\u003C/li\u003E\n\u003C/ul\u003E\n", + "Headings": [ + { + "Id": "a-common-misunderstanding", + "Text": "\u26A0\uFE0F A Common Misunderstanding", + "Level": 0 + }, + { + "Id": "layered-configuration-model", + "Text": "\uD83E\uDDE9 Layered Configuration Model", + "Level": 0 + }, + { + "Id": "core-behavior-definition", + "Text": "\uD83D\uDD39 Core (Behavior Definition)", + "Level": 1 + }, + { + "Id": "server-application-configuration", + "Text": "\uD83D\uDD39 Server (Application Configuration)", + "Level": 1 + }, + { + "Id": "client-runtime-behavior", + "Text": "\uD83D\uDD39 Client (Runtime Behavior)", + "Level": 1 + }, + { + "Id": "runtime-configuration-the-key-idea", + "Text": "\u26A1 Runtime Configuration (The Key Idea)", + "Level": 0 + }, + { + "Id": "how-it-works", + "Text": "\uD83E\uDDE0 How It Works", + "Level": 1 + }, + { + "Id": "from-static-to-dynamic", + "Text": "\uD83D\uDD04 From Static to Dynamic", + "Level": 0 + }, + { + "Id": "why-this-matters", + "Text": "\uD83C\uDFAF Why This Matters", + "Level": 0 + }, + { + "Id": "safety-by-design", + "Text": "\uD83D\uDEE1 Safety by Design", + "Level": 0 + }, + { + "Id": "core-vs-effective", + "Text": "\u2699\uFE0F Core vs Effective", + "Level": 0 + }, + { + "Id": "mental-model", + "Text": "\uD83E\uDDE0 Mental Model", + "Level": 0 + }, + { + "Id": "next-step", + "Text": "\u27A1\uFE0F Next Step", + "Level": 0 + } + ] } \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/configuration-sources.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/configuration-sources.json index 0382fd0d..1ebbf96d 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/configuration-sources.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/configuration-sources.json @@ -1,5 +1,87 @@ { "Slug": "configuration/configuration-sources", "Title": "Configuration Sources", - "Html": "\n\u003Cp\u003EUltimateAuth supports multiple configuration sources.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 But more importantly, it defines a \u003Cstrong\u003Eclear and predictable precedence model\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022two-ways-to-configure\u0022\u003E\uD83E\uDDE0 Two Ways to Configure\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth can be configured using:\u003C/p\u003E\n\u003Ch3 id=\u0022code-program.cs\u0022\u003E\uD83D\uDD39 Code (Program.cs)\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthServer(o =\u0026gt;\n{\n o.Login.MaxFailedAttempts = 5;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022configuration-files-appsettings.json\u0022\u003E\uD83D\uDD39 Configuration Files (appsettings.json)\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-json\u0022\u003E{\n \u0026quot;UltimateAuth\u0026quot;: {\n \u0026quot;Server\u0026quot;: {\n \u0026quot;Login\u0026quot;: {\n \u0026quot;MaxFailedAttempts\u0026quot;: 5\n }\n }\n }\n}\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022precedence-rules\u0022\u003E\u2696\uFE0F Precedence Rules\u003C/h2\u003E\n\u003Cp\u003EThis is the most important rule:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Cstrong\u003EConfiguration files override code\u003C/strong\u003E\u003C/p\u003E\n\u003Cp\u003EExecution order:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003EProgram.cs configuration\n \u2193\nappsettings.json binding\n \u2193\nFinal options\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This means:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EDefaults can be defined in code\u003C/li\u003E\n\u003Cli\u003EEnvironments can override them safely\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022environment-based-configuration\u0022\u003E\uD83C\uDF0D Environment-Based Configuration\u003C/h2\u003E\n\u003Cp\u003EASP.NET Core supports environment-specific configuration:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eappsettings.Development.json\u003C/li\u003E\n\u003Cli\u003Eappsettings.Staging.json\u003C/li\u003E\n\u003Cli\u003Eappsettings.Production.json\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EExample:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-json\u0022\u003E{\n \u0026quot;UltimateAuth\u0026quot;: {\n \u0026quot;Server\u0026quot;: {\n \u0026quot;Session\u0026quot;: {\n \u0026quot;IdleTimeout\u0026quot;: \u0026quot;7.00:00:00\u0026quot;\n }\n }\n }\n}\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 You can use different values per environment without changing code\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022recommended-strategy\u0022\u003E\uD83E\uDDE9 Recommended Strategy\u003C/h2\u003E\n\u003Cp\u003EFor real-world applications:\u003C/p\u003E\n\u003Ch3 id=\u0022use-program.cs-for\u0022\u003E\u2714 Use Program.cs for:\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EDefaults\u003C/li\u003E\n\u003Cli\u003EDevelopment setup\u003C/li\u003E\n\u003Cli\u003ELocal testing\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022use-appsettings-for\u0022\u003E\u2714 Use appsettings for:\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EEnvironment-specific overrides\u003C/li\u003E\n\u003Cli\u003EProduction tuning\u003C/li\u003E\n\u003Cli\u003EDeployment configuration\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This keeps your system flexible and maintainable\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022safety-validation\u0022\u003E\uD83D\uDEE1 Safety \u0026amp; Validation\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth validates configuration at startup:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EInvalid combinations are rejected\u003C/li\u003E\n\u003Cli\u003EMissing required values fail fast\u003C/li\u003E\n\u003Cli\u003EUnsafe configurations are blocked\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You will not run with a broken configuration\u003C/p\u003E\n\u003Ch2 id=\u0022common-pitfalls\u0022\u003E\u26A0\uFE0F Common Pitfalls\u003C/h2\u003E\n\u003Ch3 id=\u0022assuming-code-overrides-config\u0022\u003E\u274C Assuming code overrides config\u003C/h3\u003E\n\u003Cp\u003EIt does not.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 appsettings.json always wins\u003C/p\u003E\n\u003Ch3 id=\u0022hardcoding-production-values\u0022\u003E\u274C Hardcoding production values\u003C/h3\u003E\n\u003Cp\u003EAvoid:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.Token.AccessTokenLifetime = TimeSpan.FromHours(1);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Use environment config instead for maximum flexibility\u003C/p\u003E\n\u003Ch3 id=\u0022mixing-environments-unintentionally\u0022\u003E\u274C Mixing environments unintentionally\u003C/h3\u003E\n\u003Cp\u003EEnsure correct environment is set:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003EASPNETCORE_ENVIRONMENT=Production\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Code defines defaults\u003Cbr /\u003E\n\uD83D\uDC49 Configuration defines reality\u003C/p\u003E\n\u003Ch2 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EUltimateAuth supports code \u002B configuration\u003C/li\u003E\n\u003Cli\u003Eappsettings.json overrides Program.cs\u003C/li\u003E\n\u003Cli\u003EEnvironment-based configuration is first-class\u003C/li\u003E\n\u003Cli\u003EValidation prevents unsafe setups\u003C/li\u003E\n\u003Cli\u003EDesigned for real-world deployment\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003EAdvanced Configuration\u003C/strong\u003E\u003C/p\u003E\n" + "Html": "\n\u003Cp\u003EUltimateAuth supports multiple configuration sources.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 But more importantly, it defines a \u003Cstrong\u003Eclear and predictable precedence model\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022two-ways-to-configure\u0022\u003E\uD83E\uDDE0 Two Ways to Configure\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth can be configured using:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022code-program.cs\u0022\u003E\uD83D\uDD39 Code (Program.cs)\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthServer(o =\u0026gt;\n{\n o.Login.MaxFailedAttempts = 5;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022configuration-files-appsettings.json\u0022\u003E\uD83D\uDD39 Configuration Files (appsettings.json)\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-json\u0022\u003E{\n \u0026quot;UltimateAuth\u0026quot;: {\n \u0026quot;Server\u0026quot;: {\n \u0026quot;Login\u0026quot;: {\n \u0026quot;MaxFailedAttempts\u0026quot;: 5\n }\n }\n }\n}\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022precedence-rules\u0022\u003E\u2696\uFE0F Precedence Rules\u003C/h2\u003E\n\u003Cp\u003EThis is the most important rule:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Cstrong\u003EConfiguration files override code\u003C/strong\u003E\u003C/p\u003E\n\u003Cp\u003EExecution order:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003EProgram.cs configuration\n \u2193\nappsettings.json binding\n \u2193\nFinal options\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This means:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EDefaults can be defined in code\u003C/li\u003E\n\u003Cli\u003EEnvironments can override them safely\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022environment-based-configuration\u0022\u003E\uD83C\uDF0D Environment-Based Configuration\u003C/h2\u003E\n\u003Cp\u003EASP.NET Core supports environment-specific configuration:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eappsettings.Development.json\u003C/li\u003E\n\u003Cli\u003Eappsettings.Staging.json\u003C/li\u003E\n\u003Cli\u003Eappsettings.Production.json\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EExample:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-json\u0022\u003E{\n \u0026quot;UltimateAuth\u0026quot;: {\n \u0026quot;Server\u0026quot;: {\n \u0026quot;Session\u0026quot;: {\n \u0026quot;IdleTimeout\u0026quot;: \u0026quot;7.00:00:00\u0026quot;\n }\n }\n }\n}\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 You can use different values per environment without changing code\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022recommended-strategy\u0022\u003E\uD83E\uDDE9 Recommended Strategy\u003C/h2\u003E\n\u003Cp\u003EFor real-world applications:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022use-program.cs-for\u0022\u003E\u2714 Use Program.cs for:\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EDefaults\u003C/li\u003E\n\u003Cli\u003EDevelopment setup\u003C/li\u003E\n\u003Cli\u003ELocal testing\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022use-appsettings-for\u0022\u003E\u2714 Use appsettings for:\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EEnvironment-specific overrides\u003C/li\u003E\n\u003Cli\u003EProduction tuning\u003C/li\u003E\n\u003Cli\u003EDeployment configuration\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This keeps your system flexible and maintainable\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022safety-validation\u0022\u003E\uD83D\uDEE1 Safety \u0026amp; Validation\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth validates configuration at startup:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EInvalid combinations are rejected\u003C/li\u003E\n\u003Cli\u003EMissing required values fail fast\u003C/li\u003E\n\u003Cli\u003EUnsafe configurations are blocked\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You will not run with a broken configuration\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022common-pitfalls\u0022\u003E\u26A0\uFE0F Common Pitfalls\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022assuming-code-overrides-config\u0022\u003E\u274C Assuming code overrides config\u003C/h3\u003E\n\u003Cp\u003EIt does not.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 appsettings.json always wins\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022hardcoding-production-values\u0022\u003E\u274C Hardcoding production values\u003C/h3\u003E\n\u003Cp\u003EAvoid:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.Token.AccessTokenLifetime = TimeSpan.FromHours(1);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Use environment config instead for maximum flexibility\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022mixing-environments-unintentionally\u0022\u003E\u274C Mixing environments unintentionally\u003C/h3\u003E\n\u003Cp\u003EEnsure correct environment is set:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003EASPNETCORE_ENVIRONMENT=Production\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Code defines defaults\u003Cbr /\u003E\n\uD83D\uDC49 Configuration defines reality\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EUltimateAuth supports code \u002B configuration\u003C/li\u003E\n\u003Cli\u003Eappsettings.json overrides Program.cs\u003C/li\u003E\n\u003Cli\u003EEnvironment-based configuration is first-class\u003C/li\u003E\n\u003Cli\u003EValidation prevents unsafe setups\u003C/li\u003E\n\u003Cli\u003EDesigned for real-world deployment\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003EAdvanced Configuration\u003C/strong\u003E\u003C/p\u003E\n", + "Headings": [ + { + "Id": "two-ways-to-configure", + "Text": "\uD83E\uDDE0 Two Ways to Configure", + "Level": 0 + }, + { + "Id": "code-program.cs", + "Text": "\uD83D\uDD39 Code (Program.cs)", + "Level": 1 + }, + { + "Id": "configuration-files-appsettings.json", + "Text": "\uD83D\uDD39 Configuration Files (appsettings.json)", + "Level": 1 + }, + { + "Id": "precedence-rules", + "Text": "\u2696\uFE0F Precedence Rules", + "Level": 0 + }, + { + "Id": "environment-based-configuration", + "Text": "\uD83C\uDF0D Environment-Based Configuration", + "Level": 0 + }, + { + "Id": "recommended-strategy", + "Text": "\uD83E\uDDE9 Recommended Strategy", + "Level": 0 + }, + { + "Id": "use-program.cs-for", + "Text": "\u2714 Use Program.cs for:", + "Level": 1 + }, + { + "Id": "use-appsettings-for", + "Text": "\u2714 Use appsettings for:", + "Level": 1 + }, + { + "Id": "safety-validation", + "Text": "\uD83D\uDEE1 Safety \u0026 Validation", + "Level": 0 + }, + { + "Id": "common-pitfalls", + "Text": "\u26A0\uFE0F Common Pitfalls", + "Level": 0 + }, + { + "Id": "assuming-code-overrides-config", + "Text": "\u274C Assuming code overrides config", + "Level": 1 + }, + { + "Id": "hardcoding-production-values", + "Text": "\u274C Hardcoding production values", + "Level": 1 + }, + { + "Id": "mixing-environments-unintentionally", + "Text": "\u274C Mixing environments unintentionally", + "Level": 1 + }, + { + "Id": "mental-model", + "Text": "\uD83E\uDDE0 Mental Model", + "Level": 0 + }, + { + "Id": "key-takeaways", + "Text": "\uD83D\uDCCC Key Takeaways", + "Level": 0 + }, + { + "Id": "next-step", + "Text": "\u27A1\uFE0F Next Step", + "Level": 0 + } + ] } \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/index.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/index.json index e42cc166..fb345771 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/index.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/index.json @@ -1,5 +1,57 @@ { "Slug": "configuration/index", "Title": "Configuration", - "Html": "\n\u003Cp\u003EUltimateAuth is designed to be flexible.\u003C/p\u003E\n\u003Cp\u003EBut flexibility without structure leads to chaos.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Configuration in UltimateAuth is structured, layered, and safe by default.\u003C/p\u003E\n\u003Ch2 id=\u0022what-you-configure\u0022\u003E\uD83E\uDDE0 What You Configure\u003C/h2\u003E\n\u003Cp\u003EIn UltimateAuth, you don\u2019t just configure values.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 You configure behavior.\u003C/p\u003E\n\u003Cp\u003EThis includes:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EHow sessions are created and managed\u003C/li\u003E\n\u003Cli\u003EHow tokens are issued and refreshed\u003C/li\u003E\n\u003Cli\u003EHow tenants are resolved\u003C/li\u003E\n\u003Cli\u003EHow clients interact with the system\u003C/li\u003E\n\u003Cli\u003EWhich features are enabled or restricted\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022configuration-layers\u0022\u003E\uD83E\uDDE9 Configuration Layers\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth separates configuration into three layers:\u003C/p\u003E\n\u003Ch3 id=\u0022core\u0022\u003E\uD83D\uDD39 Core\u003C/h3\u003E\n\u003Cp\u003EDefines authentication behavior:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession lifecycle\u003C/li\u003E\n\u003Cli\u003EToken policies\u003C/li\u003E\n\u003Cli\u003EPKCE flows\u003C/li\u003E\n\u003Cli\u003EMulti-tenancy\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022server\u0022\u003E\uD83D\uDD39 Server\u003C/h3\u003E\n\u003Cp\u003EDefines runtime behavior:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAllowed authentication modes\u003C/li\u003E\n\u003Cli\u003EEndpoint exposure\u003C/li\u003E\n\u003Cli\u003ECookie and transport behavior\u003C/li\u003E\n\u003Cli\u003EHub deployment\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022client\u0022\u003E\uD83D\uDD39 Client\u003C/h3\u003E\n\u003Cp\u003EDefines client-side behavior:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EClient profile\u003C/li\u003E\n\u003Cli\u003EPKCE configuration\u003C/li\u003E\n\u003Cli\u003EAuto-refresh and re-authentication\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 These layers are independent but work together.\u003C/p\u003E\n\u003Ch2 id=\u0022configuration-sources\u0022\u003E\u2699\uFE0F Configuration Sources\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth supports two configuration styles:\u003C/p\u003E\n\u003Ch3 id=\u0022code-based-program.cs\u0022\u003ECode-based (Program.cs)\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthServer(o =\u0026gt;\n{\n o.Login.MaxFailedAttempts = 5;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022configuration-based-appsettings.json\u0022\u003EConfiguration-based (appsettings.json)\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003E{\n \u0026quot;UltimateAuth\u0026quot;: {\n \u0026quot;Server\u0026quot;: {\n \u0026quot;Login\u0026quot;: {\n \u0026quot;MaxFailedAttempts\u0026quot;: 5\n }\n }\n }\n}\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 appsettings.json overrides Program.cs\u003C/p\u003E\n\u003Cp\u003EThis allows:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEnvironment-based configuration\u003C/li\u003E\n\u003Cli\u003ECentralized management\u003C/li\u003E\n\u003Cli\u003EProduction-safe overrides\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022safety-by-design\u0022\u003E\uD83D\uDEE1 Safety by Design\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth does not allow unsafe configurations silently.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EInvalid combinations fail at startup\u003C/li\u003E\n\u003Cli\u003EUnsupported modes are rejected\u003C/li\u003E\n\u003Cli\u003ESecurity invariants are enforced\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Flexibility is allowed\n\uD83D\uDC49 Unsafe behavior is not\u003C/p\u003E\n\u003Ch2 id=\u0022whats-next\u0022\u003E\uD83C\uDFAF What\u2019s Next?\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EUnderstand configuration layers \u2192 Configuration Overview\u003C/li\u003E\n\u003Cli\u003ELearn Core behavior \u2192 Core Options\u003C/li\u003E\n\u003Cli\u003ECustomize server \u2192 Server Options\u003C/li\u003E\n\u003Cli\u003EControl clients \u2192 Client Options\u003C/li\u003E\n\u003Cli\u003EGo deeper \u2192 Advanced Configuration\u003C/li\u003E\n\u003C/ul\u003E\n" + "Html": "\n\u003Cp\u003EUltimateAuth is designed to be flexible.\u003C/p\u003E\n\u003Cp\u003EBut flexibility without structure leads to chaos.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Configuration in UltimateAuth is structured, layered, and safe by default.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-you-configure\u0022\u003E\uD83E\uDDE0 What You Configure\u003C/h2\u003E\n\u003Cp\u003EIn UltimateAuth, you don\u2019t just configure values.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 You configure behavior.\u003C/p\u003E\n\u003Cp\u003EThis includes:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EHow sessions are created and managed\u003C/li\u003E\n\u003Cli\u003EHow tokens are issued and refreshed\u003C/li\u003E\n\u003Cli\u003EHow tenants are resolved\u003C/li\u003E\n\u003Cli\u003EHow clients interact with the system\u003C/li\u003E\n\u003Cli\u003EWhich features are enabled or restricted\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022configuration-layers\u0022\u003E\uD83E\uDDE9 Configuration Layers\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth separates configuration into three layers:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022core\u0022\u003E\uD83D\uDD39 Core\u003C/h3\u003E\n\u003Cp\u003EDefines authentication behavior:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession lifecycle\u003C/li\u003E\n\u003Cli\u003EToken policies\u003C/li\u003E\n\u003Cli\u003EPKCE flows\u003C/li\u003E\n\u003Cli\u003EMulti-tenancy\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022server\u0022\u003E\uD83D\uDD39 Server\u003C/h3\u003E\n\u003Cp\u003EDefines runtime behavior:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAllowed authentication modes\u003C/li\u003E\n\u003Cli\u003EEndpoint exposure\u003C/li\u003E\n\u003Cli\u003ECookie and transport behavior\u003C/li\u003E\n\u003Cli\u003EHub deployment\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022client\u0022\u003E\uD83D\uDD39 Client\u003C/h3\u003E\n\u003Cp\u003EDefines client-side behavior:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EClient profile\u003C/li\u003E\n\u003Cli\u003EPKCE configuration\u003C/li\u003E\n\u003Cli\u003EAuto-refresh and re-authentication\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 These layers are independent but work together.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022configuration-sources\u0022\u003E\u2699\uFE0F Configuration Sources\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth supports two configuration styles:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022code-based-program.cs\u0022\u003ECode-based (Program.cs)\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthServer(o =\u0026gt;\n{\n o.Login.MaxFailedAttempts = 5;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022configuration-based-appsettings.json\u0022\u003EConfiguration-based (appsettings.json)\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003E{\n \u0026quot;UltimateAuth\u0026quot;: {\n \u0026quot;Server\u0026quot;: {\n \u0026quot;Login\u0026quot;: {\n \u0026quot;MaxFailedAttempts\u0026quot;: 5\n }\n }\n }\n}\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 appsettings.json overrides Program.cs\u003C/p\u003E\n\u003Cp\u003EThis allows:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEnvironment-based configuration\u003C/li\u003E\n\u003Cli\u003ECentralized management\u003C/li\u003E\n\u003Cli\u003EProduction-safe overrides\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022safety-by-design\u0022\u003E\uD83D\uDEE1 Safety by Design\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth does not allow unsafe configurations silently.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EInvalid combinations fail at startup\u003C/li\u003E\n\u003Cli\u003EUnsupported modes are rejected\u003C/li\u003E\n\u003Cli\u003ESecurity invariants are enforced\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Flexibility is allowed\n\uD83D\uDC49 Unsafe behavior is not\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022whats-next\u0022\u003E\uD83C\uDFAF What\u2019s Next?\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EUnderstand configuration layers \u2192 Configuration Overview\u003C/li\u003E\n\u003Cli\u003ELearn Core behavior \u2192 Core Options\u003C/li\u003E\n\u003Cli\u003ECustomize server \u2192 Server Options\u003C/li\u003E\n\u003Cli\u003EControl clients \u2192 Client Options\u003C/li\u003E\n\u003Cli\u003EGo deeper \u2192 Advanced Configuration\u003C/li\u003E\n\u003C/ul\u003E\n", + "Headings": [ + { + "Id": "what-you-configure", + "Text": "\uD83E\uDDE0 What You Configure", + "Level": 0 + }, + { + "Id": "configuration-layers", + "Text": "\uD83E\uDDE9 Configuration Layers", + "Level": 0 + }, + { + "Id": "core", + "Text": "\uD83D\uDD39 Core", + "Level": 1 + }, + { + "Id": "server", + "Text": "\uD83D\uDD39 Server", + "Level": 1 + }, + { + "Id": "client", + "Text": "\uD83D\uDD39 Client", + "Level": 1 + }, + { + "Id": "configuration-sources", + "Text": "\u2699\uFE0F Configuration Sources", + "Level": 0 + }, + { + "Id": "code-based-program.cs", + "Text": "Code-based (Program.cs)", + "Level": 1 + }, + { + "Id": "configuration-based-appsettings.json", + "Text": "Configuration-based (appsettings.json)", + "Level": 1 + }, + { + "Id": "safety-by-design", + "Text": "\uD83D\uDEE1 Safety by Design", + "Level": 0 + }, + { + "Id": "whats-next", + "Text": "\uD83C\uDFAF What\u2019s Next?", + "Level": 0 + } + ] } \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/server-options.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/server-options.json index 4152d2fa..8804d48a 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/server-options.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/configuration/server-options.json @@ -1,5 +1,77 @@ { "Slug": "configuration/server-options", "Title": "Server Options", - "Html": "\n\u003Cp\u003EUltimateAuth is configured primarily through \u003Cstrong\u003EServer Options\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 This is the main entry point for configuring authentication behavior.\u003C/p\u003E\n\u003Ch2 id=\u0022what-are-server-options\u0022\u003E\uD83E\uDDE0 What Are Server Options?\u003C/h2\u003E\n\u003Cp\u003EServer options define how UltimateAuth behaves \u003Cstrong\u003Einside your application\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EThey control:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAuthentication behavior\u003C/li\u003E\n\u003Cli\u003ESecurity policies\u003C/li\u003E\n\u003Cli\u003EToken issuance\u003C/li\u003E\n\u003Cli\u003ESession lifecycle\u003C/li\u003E\n\u003Cli\u003EEndpoint exposure\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022basic-usage\u0022\u003E\u2699\uFE0F Basic Usage\u003C/h2\u003E\n\u003Cp\u003EYou configure server options in \u003Ccode\u003EProgram.cs\u003C/code\u003E:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthServer(o =\u0026gt;\n{\n o.Login.MaxFailedAttempts = 5;\n o.Session.IdleTimeout = TimeSpan.FromDays(7);\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Chr /\u003E\n\u003Cp\u003EYou can also use \u003Ccode\u003Eappsettings.json\u003C/code\u003E:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-json\u0022\u003E{\n \u0026quot;UltimateAuth\u0026quot;: {\n \u0026quot;Server\u0026quot;: {\n \u0026quot;Login\u0026quot;: {\n \u0026quot;MaxFailedAttempts\u0026quot;: 5\n },\n \u0026quot;Session\u0026quot;: {\n \u0026quot;IdleTimeout\u0026quot;: \u0026quot;07.00.00.00\u0026quot;\n }\n }\n }\n}\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ccode\u003Eappsettings.json\u003C/code\u003E overrides \u003Ccode\u003EProgram.cs\u003C/code\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022core-composition\u0022\u003E\uD83E\uDDE9 Core Composition\u003C/h2\u003E\n\u003Cp\u003EServer options include Core behavior:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogin\u003C/li\u003E\n\u003Cli\u003ESession\u003C/li\u003E\n\u003Cli\u003EToken\u003C/li\u003E\n\u003Cli\u003EPKCE\u003C/li\u003E\n\u003Cli\u003EMulti-tenancy\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 These are defined in Core\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 But configured via Server\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022important-you-dont-configure-modes-directly\u0022\u003E\u26A0\uFE0F Important: You Don\u2019t Configure Modes Directly\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth does NOT expect you to select a single auth mode.\u003C/p\u003E\n\u003Cp\u003EInstead:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Mode is resolved at runtime\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022allowed-modes-guardrail\u0022\u003E\uD83D\uDEE1 Allowed Modes (Guardrail)\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.AllowedModes = new[]\n{\n UAuthMode.Hybrid,\n UAuthMode.PureOpaque\n};\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This does NOT select a mode\u003Cbr /\u003E\n\uD83D\uDC49 It restricts which modes are allowed\u003C/p\u003E\n\u003Cp\u003EIf a resolved mode is not allowed:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Request fails early\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022runtime-behavior-effective-options\u0022\u003E\u26A1 Runtime Behavior (Effective Options)\u003C/h2\u003E\n\u003Cp\u003EServer options are not used directly.\u003C/p\u003E\n\u003Cp\u003EThey are transformed into:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ccode\u003EEffectiveUAuthServerOptions\u003C/code\u003E\u003C/p\u003E\n\u003Cp\u003EThis happens per request:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EMode is resolved\u003C/li\u003E\n\u003Cli\u003EDefaults are applied\u003C/li\u003E\n\u003Cli\u003EOverrides are applied\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 What actually runs is \u003Cstrong\u003EEffective Options\u003C/strong\u003E\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mode-based-defaults\u0022\u003E\uD83D\uDD04 Mode-Based Defaults\u003C/h2\u003E\n\u003Cp\u003EEach auth mode applies different defaults automatically:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EPureOpaque \u2192 session-heavy\u003C/li\u003E\n\u003Cli\u003EHybrid \u2192 session \u002B token\u003C/li\u003E\n\u003Cli\u003EPureJwt \u2192 token-only\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You don\u2019t need to manually configure everything\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022endpoint-control\u0022\u003E\uD83C\uDF9B Endpoint Control\u003C/h2\u003E\n\u003Cp\u003EYou can control which features are enabled:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.Endpoints.Authentication = true;\no.Endpoints.Session = true;\no.Endpoints.Authorization = true;\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EYou can also disable specific actions:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.Endpoints.DisabledActions.Add(\u0026quot;UAuthActions.Users.Create.Anonymous\u0026quot;);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Useful for API hardening\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022cookie-transport-behavior\u0022\u003E\uD83C\uDF6A Cookie \u0026amp; Transport Behavior\u003C/h2\u003E\n\u003Cp\u003EServer options define how credentials are transported:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECookies\u003C/li\u003E\n\u003Cli\u003EHeaders\u003C/li\u003E\n\u003Cli\u003ETokens\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Unsafe combinations are rejected at startup\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022hub-configuration\u0022\u003E\uD83C\uDF10 Hub Configuration\u003C/h2\u003E\n\u003Cp\u003EIf using UAuthHub:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.HubDeploymentMode = UAuthHubDeploymentMode.Integrated;\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Defines how auth server is deployed\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022session-resolution\u0022\u003E\uD83D\uDD01 Session Resolution\u003C/h2\u003E\n\u003Cp\u003EControls how session IDs are extracted:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECookie\u003C/li\u003E\n\u003Cli\u003EHeader\u003C/li\u003E\n\u003Cli\u003EBearer\u003C/li\u003E\n\u003Cli\u003EQuery\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Fully configurable\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Server options define \u003Cstrong\u003Ewhat is allowed\u003C/strong\u003E\u003Cbr /\u003E\n\uD83D\uDC49 Runtime determines \u003Cstrong\u003Ewhat is used\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EServer options are the main configuration entry\u003C/li\u003E\n\u003Cli\u003ECore behavior is configured via server\u003C/li\u003E\n\u003Cli\u003EModes are not selected manually\u003C/li\u003E\n\u003Cli\u003EEffective options are computed per request\u003C/li\u003E\n\u003Cli\u003ESecurity is enforced by design\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Ch2 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003EClient Options\u003C/strong\u003E\u003C/p\u003E\n" + "Html": "\n\u003Cp\u003EUltimateAuth is configured primarily through \u003Cstrong\u003EServer Options\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 This is the main entry point for configuring authentication behavior.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-are-server-options\u0022\u003E\uD83E\uDDE0 What Are Server Options?\u003C/h2\u003E\n\u003Cp\u003EServer options define how UltimateAuth behaves \u003Cstrong\u003Einside your application\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EThey control:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAuthentication behavior\u003C/li\u003E\n\u003Cli\u003ESecurity policies\u003C/li\u003E\n\u003Cli\u003EToken issuance\u003C/li\u003E\n\u003Cli\u003ESession lifecycle\u003C/li\u003E\n\u003Cli\u003EEndpoint exposure\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022basic-usage\u0022\u003E\u2699\uFE0F Basic Usage\u003C/h2\u003E\n\u003Cp\u003EYou configure server options in \u003Ccode\u003EProgram.cs\u003C/code\u003E:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthServer(o =\u0026gt;\n{\n o.Login.MaxFailedAttempts = 5;\n o.Session.IdleTimeout = TimeSpan.FromDays(7);\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Chr /\u003E\n\u003Cp\u003EYou can also use \u003Ccode\u003Eappsettings.json\u003C/code\u003E:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-json\u0022\u003E{\n \u0026quot;UltimateAuth\u0026quot;: {\n \u0026quot;Server\u0026quot;: {\n \u0026quot;Login\u0026quot;: {\n \u0026quot;MaxFailedAttempts\u0026quot;: 5\n },\n \u0026quot;Session\u0026quot;: {\n \u0026quot;IdleTimeout\u0026quot;: \u0026quot;07.00.00.00\u0026quot;\n }\n }\n }\n}\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ccode\u003Eappsettings.json\u003C/code\u003E overrides \u003Ccode\u003EProgram.cs\u003C/code\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022core-composition\u0022\u003E\uD83E\uDDE9 Core Composition\u003C/h2\u003E\n\u003Cp\u003EServer options include Core behavior:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogin\u003C/li\u003E\n\u003Cli\u003ESession\u003C/li\u003E\n\u003Cli\u003EToken\u003C/li\u003E\n\u003Cli\u003EPKCE\u003C/li\u003E\n\u003Cli\u003EMulti-tenancy\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 These are defined in Core\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 But configured via Server\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022important-you-dont-configure-modes-directly\u0022\u003E\u26A0\uFE0F Important: You Don\u2019t Configure Modes Directly\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth does NOT expect you to select a single auth mode.\u003C/p\u003E\n\u003Cp\u003EInstead:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Mode is resolved at runtime\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022allowed-modes-guardrail\u0022\u003E\uD83D\uDEE1 Allowed Modes (Guardrail)\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.AllowedModes = new[]\n{\n UAuthMode.Hybrid,\n UAuthMode.PureOpaque\n};\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This does NOT select a mode\u003Cbr /\u003E\n\uD83D\uDC49 It restricts which modes are allowed\u003C/p\u003E\n\u003Cp\u003EIf a resolved mode is not allowed:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Request fails early\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022runtime-behavior-effective-options\u0022\u003E\u26A1 Runtime Behavior (Effective Options)\u003C/h2\u003E\n\u003Cp\u003EServer options are not used directly.\u003C/p\u003E\n\u003Cp\u003EThey are transformed into:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ccode\u003EEffectiveUAuthServerOptions\u003C/code\u003E\u003C/p\u003E\n\u003Cp\u003EThis happens per request:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EMode is resolved\u003C/li\u003E\n\u003Cli\u003EDefaults are applied\u003C/li\u003E\n\u003Cli\u003EOverrides are applied\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 What actually runs is \u003Cstrong\u003EEffective Options\u003C/strong\u003E\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mode-based-defaults\u0022\u003E\uD83D\uDD04 Mode-Based Defaults\u003C/h2\u003E\n\u003Cp\u003EEach auth mode applies different defaults automatically:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EPureOpaque \u2192 session-heavy\u003C/li\u003E\n\u003Cli\u003EHybrid \u2192 session \u002B token\u003C/li\u003E\n\u003Cli\u003EPureJwt \u2192 token-only\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You don\u2019t need to manually configure everything\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022endpoint-control\u0022\u003E\uD83C\uDF9B Endpoint Control\u003C/h2\u003E\n\u003Cp\u003EYou can control which features are enabled:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.Endpoints.Authentication = true;\no.Endpoints.Session = true;\no.Endpoints.Authorization = true;\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EYou can also disable specific actions:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.Endpoints.DisabledActions.Add(\u0026quot;UAuthActions.Users.Create.Anonymous\u0026quot;);\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Useful for API hardening\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022cookie-transport-behavior\u0022\u003E\uD83C\uDF6A Cookie \u0026amp; Transport Behavior\u003C/h2\u003E\n\u003Cp\u003EServer options define how credentials are transported:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECookies\u003C/li\u003E\n\u003Cli\u003EHeaders\u003C/li\u003E\n\u003Cli\u003ETokens\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Unsafe combinations are rejected at startup\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022hub-configuration\u0022\u003E\uD83C\uDF10 Hub Configuration\u003C/h2\u003E\n\u003Cp\u003EIf using UAuthHub:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eo.HubDeploymentMode = UAuthHubDeploymentMode.Integrated;\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Defines how auth server is deployed\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022session-resolution\u0022\u003E\uD83D\uDD01 Session Resolution\u003C/h2\u003E\n\u003Cp\u003EControls how session IDs are extracted:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECookie\u003C/li\u003E\n\u003Cli\u003EHeader\u003C/li\u003E\n\u003Cli\u003EBearer\u003C/li\u003E\n\u003Cli\u003EQuery\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Fully configurable\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Server options define \u003Cstrong\u003Ewhat is allowed\u003C/strong\u003E\u003Cbr /\u003E\n\uD83D\uDC49 Runtime determines \u003Cstrong\u003Ewhat is used\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EServer options are the main configuration entry\u003C/li\u003E\n\u003Cli\u003ECore behavior is configured via server\u003C/li\u003E\n\u003Cli\u003EModes are not selected manually\u003C/li\u003E\n\u003Cli\u003EEffective options are computed per request\u003C/li\u003E\n\u003Cli\u003ESecurity is enforced by design\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003EClient Options\u003C/strong\u003E\u003C/p\u003E\n", + "Headings": [ + { + "Id": "what-are-server-options", + "Text": "\uD83E\uDDE0 What Are Server Options?", + "Level": 0 + }, + { + "Id": "basic-usage", + "Text": "\u2699\uFE0F Basic Usage", + "Level": 0 + }, + { + "Id": "core-composition", + "Text": "\uD83E\uDDE9 Core Composition", + "Level": 0 + }, + { + "Id": "important-you-dont-configure-modes-directly", + "Text": "\u26A0\uFE0F Important: You Don\u2019t Configure Modes Directly", + "Level": 0 + }, + { + "Id": "allowed-modes-guardrail", + "Text": "\uD83D\uDEE1 Allowed Modes (Guardrail)", + "Level": 0 + }, + { + "Id": "runtime-behavior-effective-options", + "Text": "\u26A1 Runtime Behavior (Effective Options)", + "Level": 0 + }, + { + "Id": "mode-based-defaults", + "Text": "\uD83D\uDD04 Mode-Based Defaults", + "Level": 0 + }, + { + "Id": "endpoint-control", + "Text": "\uD83C\uDF9B Endpoint Control", + "Level": 0 + }, + { + "Id": "cookie-transport-behavior", + "Text": "\uD83C\uDF6A Cookie \u0026 Transport Behavior", + "Level": 0 + }, + { + "Id": "hub-configuration", + "Text": "\uD83C\uDF10 Hub Configuration", + "Level": 0 + }, + { + "Id": "session-resolution", + "Text": "\uD83D\uDD01 Session Resolution", + "Level": 0 + }, + { + "Id": "mental-model", + "Text": "\uD83E\uDDE0 Mental Model", + "Level": 0 + }, + { + "Id": "key-takeaways", + "Text": "\uD83D\uDCCC Key Takeaways", + "Level": 0 + }, + { + "Id": "next-step", + "Text": "\u27A1\uFE0F Next Step", + "Level": 0 + } + ] } \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/auth-model.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/auth-model.json index df5cbbf6..0e9e9033 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/auth-model.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/auth-model.json @@ -1,5 +1,92 @@ { "Slug": "fundamentals/auth-model", "Title": "Authentication Model", - "Html": "\n\u003Cp\u003EUltimateAuth is built around a simple but powerful idea:\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003EAuthentication is not just a token.\u003Cbr /\u003E\nIt is a \u003Cstrong\u003Estructured, server-controlled session model\u003C/strong\u003E.\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cp\u003EAt the core of this model are three concepts:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Cstrong\u003ERoot\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003EChain\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003ESession\u003C/strong\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003ETogether, they form what we call:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Cstrong\u003EAuthentication Lineage\u003C/strong\u003E\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022the-big-picture\u0022\u003E\uD83D\uDD11 The Big Picture\u003C/h2\u003E\n\u003Cp\u003EInstead of treating authentication as a single token or cookie, UltimateAuth models it as a hierarchy:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003ERoot (user authority)\n\u251C\u2500\u2500 Chain (device context)\n\u2502 \u251C\u2500\u2500 Session (login instance)\n\u2502 \u251C\u2500\u2500 Session\n\u2502\n\u251C\u2500\u2500 Chain\n\u2502 \u251C\u2500\u2500 Session (login instance)\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EEach level has a distinct responsibility.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022root-the-authority\u0022\u003E\uD83E\uDDE9 Root \u2014 The Authority\u003C/h2\u003E\n\u003Cp\u003E\u003Cstrong\u003ERoot\u003C/strong\u003E represents the authentication authority of a user within a tenant.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EThere is \u003Cstrong\u003Eonly one active Root per user per tenant\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EIt defines the \u003Cstrong\u003Eglobal security state\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EIt controls all chains and sessions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022what-root-does\u0022\u003EWhat Root Does\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ETracks security version (security epoch)\u003C/li\u003E\n\u003Cli\u003EInvalidates all sessions when needed\u003C/li\u003E\n\u003Cli\u003EActs as the \u003Cstrong\u003Esource of truth\u003C/strong\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022example\u0022\u003EExample\u003C/h3\u003E\n\u003Cp\u003EIf a user changes their password:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Root is updated\u003Cbr /\u003E\n\uD83D\uDC49 All existing sessions can become invalid\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022chain-the-device-context\u0022\u003E\uD83D\uDD17 Chain \u2014 The Device Context\u003C/h2\u003E\n\u003Cp\u003E\u003Cstrong\u003EChain\u003C/strong\u003E represents a device or client context.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEach device typically has its own chain\u003C/li\u003E\n\u003Cli\u003EMultiple logins from the same device belong to the same chain\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Think of Chain as:\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003E\u201CWhere is the user logged in from?\u201D\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Ch3 id=\u0022what-chain-does\u0022\u003EWhat Chain Does\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Cp\u003EGroups sessions by device\u003C/p\u003E\n\u003C/li\u003E\n\u003Cli\u003E\u003Cp\u003EEnables \u003Cstrong\u003Edevice-level control\u003C/strong\u003E\u003C/p\u003E\n\u003C/li\u003E\n\u003Cli\u003E\u003Cp\u003EAllows actions like:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogout from one device\u003C/li\u003E\n\u003Cli\u003ERevoke a specific device\u003C/li\u003E\n\u003C/ul\u003E\n\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022session-the-authentication-instance\u0022\u003E\uD83E\uDDFE Session \u2014 The Authentication Instance\u003C/h2\u003E\n\u003Cp\u003E\u003Cstrong\u003ESession\u003C/strong\u003E is the actual authentication instance.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECreated when the user logs in\u003C/li\u003E\n\u003Cli\u003ERepresents a single authenticated state\u003C/li\u003E\n\u003Cli\u003ECarries a snapshot of the Root security version\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This is what gets validated on each request.\u003C/p\u003E\n\u003Ch3 id=\u0022what-session-does\u0022\u003EWhat Session Does\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EProves the user is authenticated\u003C/li\u003E\n\u003Cli\u003ECan be refreshed, revoked, or expired\u003C/li\u003E\n\u003Cli\u003EIs tied to a specific chain\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022how-they-work-together\u0022\u003E\uD83D\uDD04 How They Work Together\u003C/h2\u003E\n\u003Cp\u003EWhen a user logs in:\u003C/p\u003E\n\u003Col\u003E\n\u003Cli\u003ERoot is resolved (or created)\u003C/li\u003E\n\u003Cli\u003EA Chain is identified (device context)\u003C/li\u003E\n\u003Cli\u003EA new Session is created\u003C/li\u003E\n\u003C/ol\u003E\n\u003Cp\u003E\u003Ccode\u003ELogin \u2192 Root \u2192 Chain \u2192 Session\u003C/code\u003E\u003C/p\u003E\n\u003Cp\u003EOn each request:\u003C/p\u003E\n\u003Col\u003E\n\u003Cli\u003ESession is validated\u003C/li\u003E\n\u003Cli\u003EChain context is checked\u003C/li\u003E\n\u003Cli\u003ERoot security version is verified\u003C/li\u003E\n\u003C/ol\u003E\n\u003Cp\u003E\uD83D\uDC49 If any level is invalid \u2192 authentication fails\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022why-this-model-matters\u0022\u003E\uD83D\uDEE1 Why This Model Matters\u003C/h2\u003E\n\u003Cp\u003ETraditional systems rely on:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECookies\u003C/li\u003E\n\u003Cli\u003EJWT tokens\u003C/li\u003E\n\u003Cli\u003EStateless validation\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThese approaches have limitations:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ENo real session control\u003C/li\u003E\n\u003Cli\u003EWeak revocation\u003C/li\u003E\n\u003Cli\u003ENo device awareness\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Cp\u003EUltimateAuth solves this by:\u003C/p\u003E\n\u003Ch3 id=\u0022server-controlled-authentication\u0022\u003E\u2714 Server-Controlled Authentication\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ESessions are always validated server-side\u003C/li\u003E\n\u003Cli\u003ENo blind trust in tokens\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022instant-revocation\u0022\u003E\u2714 Instant Revocation\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ERevoke a session \u2192 immediate effect\u003C/li\u003E\n\u003Cli\u003ERevoke a chain \u2192 device logged out\u003C/li\u003E\n\u003Cli\u003ERevoke root \u2192 disable user (global logout)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022device-awareness\u0022\u003E\u2714 Device Awareness\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EEach device has its own chain\u003C/li\u003E\n\u003Cli\u003ESessions are bound to context\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022strong-security-model\u0022\u003E\u2714 Strong Security Model\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession carries Root security version\u003C/li\u003E\n\u003Cli\u003EOld sessions automatically become invalid\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember only one thing, remember this:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Cstrong\u003ERoot = authority\u003C/strong\u003E\u003Cbr /\u003E\n\uD83D\uDC49 \u003Cstrong\u003EChain = device\u003C/strong\u003E\u003Cbr /\u003E\n\uD83D\uDC49 \u003Cstrong\u003ESession = login\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EAuthentication is not just a token\u003C/li\u003E\n\u003Cli\u003ESessions are first-class citizens\u003C/li\u003E\n\u003Cli\u003EThe server always remains in control\u003C/li\u003E\n\u003Cli\u003EDevice and security context are built-in\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003ENow that you understand the core model:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Continue to \u003Cstrong\u003EFlow-Based Authentication\u003C/strong\u003E\u003C/p\u003E\n" + "Html": "\n\u003Cp\u003EUltimateAuth is built around a simple but powerful idea:\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003EAuthentication is not just a token.\u003Cbr /\u003E\nIt is a \u003Cstrong\u003Estructured, server-controlled session model\u003C/strong\u003E.\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cp\u003EAt the core of this model are three concepts:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Cstrong\u003ERoot\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003EChain\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003ESession\u003C/strong\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003ETogether, they form what we call:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Cstrong\u003EAuthentication Lineage\u003C/strong\u003E\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022the-big-picture\u0022\u003E\uD83D\uDD11 The Big Picture\u003C/h2\u003E\n\u003Cp\u003EInstead of treating authentication as a single token or cookie, UltimateAuth models it as a hierarchy:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003ERoot (user authority)\n\u251C\u2500\u2500 Chain (device context)\n\u2502 \u251C\u2500\u2500 Session (login instance)\n\u2502 \u251C\u2500\u2500 Session\n\u2502\n\u251C\u2500\u2500 Chain\n\u2502 \u251C\u2500\u2500 Session (login instance)\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EEach level has a distinct responsibility.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022root-the-authority\u0022\u003E\uD83E\uDDE9 Root \u2014 The Authority\u003C/h2\u003E\n\u003Cp\u003E\u003Cstrong\u003ERoot\u003C/strong\u003E represents the authentication authority of a user within a tenant.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EThere is \u003Cstrong\u003Eonly one active Root per user per tenant\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EIt defines the \u003Cstrong\u003Eglobal security state\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EIt controls all chains and sessions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-root-does\u0022\u003EWhat Root Does\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ETracks security version (security epoch)\u003C/li\u003E\n\u003Cli\u003EInvalidates all sessions when needed\u003C/li\u003E\n\u003Cli\u003EActs as the \u003Cstrong\u003Esource of truth\u003C/strong\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022example\u0022\u003EExample\u003C/h3\u003E\n\u003Cp\u003EIf a user changes their password:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Root is updated\u003Cbr /\u003E\n\uD83D\uDC49 All existing sessions can become invalid\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022chain-the-device-context\u0022\u003E\uD83D\uDD17 Chain \u2014 The Device Context\u003C/h2\u003E\n\u003Cp\u003E\u003Cstrong\u003EChain\u003C/strong\u003E represents a device or client context.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEach device typically has its own chain\u003C/li\u003E\n\u003Cli\u003EMultiple logins from the same device belong to the same chain\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Think of Chain as:\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003E\u201CWhere is the user logged in from?\u201D\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-chain-does\u0022\u003EWhat Chain Does\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Cp\u003EGroups sessions by device\u003C/p\u003E\n\u003C/li\u003E\n\u003Cli\u003E\u003Cp\u003EEnables \u003Cstrong\u003Edevice-level control\u003C/strong\u003E\u003C/p\u003E\n\u003C/li\u003E\n\u003Cli\u003E\u003Cp\u003EAllows actions like:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogout from one device\u003C/li\u003E\n\u003Cli\u003ERevoke a specific device\u003C/li\u003E\n\u003C/ul\u003E\n\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022session-the-authentication-instance\u0022\u003E\uD83E\uDDFE Session \u2014 The Authentication Instance\u003C/h2\u003E\n\u003Cp\u003E\u003Cstrong\u003ESession\u003C/strong\u003E is the actual authentication instance.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECreated when the user logs in\u003C/li\u003E\n\u003Cli\u003ERepresents a single authenticated state\u003C/li\u003E\n\u003Cli\u003ECarries a snapshot of the Root security version\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This is what gets validated on each request.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-session-does\u0022\u003EWhat Session Does\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EProves the user is authenticated\u003C/li\u003E\n\u003Cli\u003ECan be refreshed, revoked, or expired\u003C/li\u003E\n\u003Cli\u003EIs tied to a specific chain\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022how-they-work-together\u0022\u003E\uD83D\uDD04 How They Work Together\u003C/h2\u003E\n\u003Cp\u003EWhen a user logs in:\u003C/p\u003E\n\u003Col\u003E\n\u003Cli\u003ERoot is resolved (or created)\u003C/li\u003E\n\u003Cli\u003EA Chain is identified (device context)\u003C/li\u003E\n\u003Cli\u003EA new Session is created\u003C/li\u003E\n\u003C/ol\u003E\n\u003Cp\u003E\u003Ccode\u003ELogin \u2192 Root \u2192 Chain \u2192 Session\u003C/code\u003E\u003C/p\u003E\n\u003Cp\u003EOn each request:\u003C/p\u003E\n\u003Col\u003E\n\u003Cli\u003ESession is validated\u003C/li\u003E\n\u003Cli\u003EChain context is checked\u003C/li\u003E\n\u003Cli\u003ERoot security version is verified\u003C/li\u003E\n\u003C/ol\u003E\n\u003Cp\u003E\uD83D\uDC49 If any level is invalid \u2192 authentication fails\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022why-this-model-matters\u0022\u003E\uD83D\uDEE1 Why This Model Matters\u003C/h2\u003E\n\u003Cp\u003ETraditional systems rely on:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECookies\u003C/li\u003E\n\u003Cli\u003EJWT tokens\u003C/li\u003E\n\u003Cli\u003EStateless validation\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThese approaches have limitations:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ENo real session control\u003C/li\u003E\n\u003Cli\u003EWeak revocation\u003C/li\u003E\n\u003Cli\u003ENo device awareness\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Cp\u003EUltimateAuth solves this by:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022server-controlled-authentication\u0022\u003E\u2714 Server-Controlled Authentication\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ESessions are always validated server-side\u003C/li\u003E\n\u003Cli\u003ENo blind trust in tokens\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022instant-revocation\u0022\u003E\u2714 Instant Revocation\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ERevoke a session \u2192 immediate effect\u003C/li\u003E\n\u003Cli\u003ERevoke a chain \u2192 device logged out\u003C/li\u003E\n\u003Cli\u003ERevoke root \u2192 disable user (global logout)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022device-awareness\u0022\u003E\u2714 Device Awareness\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EEach device has its own chain\u003C/li\u003E\n\u003Cli\u003ESessions are bound to context\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022strong-security-model\u0022\u003E\u2714 Strong Security Model\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession carries Root security version\u003C/li\u003E\n\u003Cli\u003EOld sessions automatically become invalid\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember only one thing, remember this:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Cstrong\u003ERoot = authority\u003C/strong\u003E\u003Cbr /\u003E\n\uD83D\uDC49 \u003Cstrong\u003EChain = device\u003C/strong\u003E\u003Cbr /\u003E\n\uD83D\uDC49 \u003Cstrong\u003ESession = login\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EAuthentication is not just a token\u003C/li\u003E\n\u003Cli\u003ESessions are first-class citizens\u003C/li\u003E\n\u003Cli\u003EThe server always remains in control\u003C/li\u003E\n\u003Cli\u003EDevice and security context are built-in\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003ENow that you understand the core model:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Continue to \u003Cstrong\u003EFlow-Based Authentication\u003C/strong\u003E\u003C/p\u003E\n", + "Headings": [ + { + "Id": "the-big-picture", + "Text": "\uD83D\uDD11 The Big Picture", + "Level": 0 + }, + { + "Id": "root-the-authority", + "Text": "\uD83E\uDDE9 Root \u2014 The Authority", + "Level": 0 + }, + { + "Id": "what-root-does", + "Text": "What Root Does", + "Level": 1 + }, + { + "Id": "example", + "Text": "Example", + "Level": 1 + }, + { + "Id": "chain-the-device-context", + "Text": "\uD83D\uDD17 Chain \u2014 The Device Context", + "Level": 0 + }, + { + "Id": "what-chain-does", + "Text": "What Chain Does", + "Level": 1 + }, + { + "Id": "session-the-authentication-instance", + "Text": "\uD83E\uDDFE Session \u2014 The Authentication Instance", + "Level": 0 + }, + { + "Id": "what-session-does", + "Text": "What Session Does", + "Level": 1 + }, + { + "Id": "how-they-work-together", + "Text": "\uD83D\uDD04 How They Work Together", + "Level": 0 + }, + { + "Id": "why-this-model-matters", + "Text": "\uD83D\uDEE1 Why This Model Matters", + "Level": 0 + }, + { + "Id": "server-controlled-authentication", + "Text": "\u2714 Server-Controlled Authentication", + "Level": 1 + }, + { + "Id": "instant-revocation", + "Text": "\u2714 Instant Revocation", + "Level": 1 + }, + { + "Id": "device-awareness", + "Text": "\u2714 Device Awareness", + "Level": 1 + }, + { + "Id": "strong-security-model", + "Text": "\u2714 Strong Security Model", + "Level": 1 + }, + { + "Id": "mental-model", + "Text": "\uD83E\uDDE0 Mental Model", + "Level": 0 + }, + { + "Id": "key-takeaways", + "Text": "\uD83D\uDCCC Key Takeaways", + "Level": 0 + }, + { + "Id": "next-step", + "Text": "\u27A1\uFE0F Next Step", + "Level": 0 + } + ] } \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/auth-modes.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/auth-modes.json index ad9f4466..6904fede 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/auth-modes.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/auth-modes.json @@ -1,5 +1,87 @@ { "Slug": "fundamentals/auth-modes", "Title": "Authentication Modes", - "Html": "\u003Cblockquote\u003E\n\u003Cp\u003ENote: SemiHybrid and PureJwt modes will be available on future releases. For now you can safely use PureOpaque and Hybrid modes.\u003C/p\u003E\n\u003C/blockquote\u003E\n\n\u003Cp\u003EUltimateAuth supports multiple authentication modes.\u003C/p\u003E\n\u003Cp\u003EEach mode represents a different balance between:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESecurity\u003C/li\u003E\n\u003Cli\u003EPerformance\u003C/li\u003E\n\u003Cli\u003EControl\u003C/li\u003E\n\u003Cli\u003EClient capabilities\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You don\u2019t always choose a single model.\u003Cbr /\u003E\nUltimateAuth can adapt based on context.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022available-modes\u0022\u003E\uD83E\uDDE9 Available Modes\u003C/h2\u003E\n\u003Ch3 id=\u0022pureopaque\u0022\u003EPureOpaque\u003C/h3\u003E\n\u003Cp\u003EFully server-controlled session model.\u003C/p\u003E\n\u003Ch3 id=\u0022hybrid\u0022\u003EHybrid\u003C/h3\u003E\n\u003Cp\u003ECombines session control with token-based access.\u003C/p\u003E\n\u003Ch3 id=\u0022semihybrid\u0022\u003ESemiHybrid\u003C/h3\u003E\n\u003Cp\u003EJWT-based with server-side metadata awareness.\u003C/p\u003E\n\u003Ch3 id=\u0022purejwt\u0022\u003EPureJwt\u003C/h3\u003E\n\u003Cp\u003EFully stateless token-based authentication.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mode-comparison\u0022\u003E\u2696\uFE0F Mode Comparison\u003C/h2\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EFeature\u003C/th\u003E\n\u003Cth\u003EPureOpaque\u003C/th\u003E\n\u003Cth\u003EHybrid\u003C/th\u003E\n\u003Cth\u003ESemiHybrid\u003C/th\u003E\n\u003Cth\u003EPureJwt\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ESessionId\u003C/td\u003E\n\u003Ctd\u003ERequired\u003C/td\u003E\n\u003Ctd\u003ERequired\u003C/td\u003E\n\u003Ctd\u003EMetadata only\u003C/td\u003E\n\u003Ctd\u003ENone\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EAccess Token\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ERefresh Token\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ERevocation\u003C/td\u003E\n\u003Ctd\u003EImmediate\u003C/td\u003E\n\u003Ctd\u003EImmediate\u003C/td\u003E\n\u003Ctd\u003EMetadata-based\u003C/td\u003E\n\u003Ctd\u003ENot immediate\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EStatefulness\u003C/td\u003E\n\u003Ctd\u003EFull\u003C/td\u003E\n\u003Ctd\u003EHybrid\u003C/td\u003E\n\u003Ctd\u003ESemi\u003C/td\u003E\n\u003Ctd\u003EStateless\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EServer Control\u003C/td\u003E\n\u003Ctd\u003EFull\u003C/td\u003E\n\u003Ctd\u003EHigh\u003C/td\u003E\n\u003Ctd\u003EMedium\u003C/td\u003E\n\u003Ctd\u003ELow\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EPerformance*\u003C/td\u003E\n\u003Ctd\u003EMedium\u003C/td\u003E\n\u003Ctd\u003EMedium\u003C/td\u003E\n\u003Ctd\u003EHigh\u003C/td\u003E\n\u003Ctd\u003EHighest\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EOffline Support\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003EPartial\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003E\u26A1 \u003Cstrong\u003EPerformance Note\u003C/strong\u003E\u003C/p\u003E\n\u003Cp\u003EAll modes in UltimateAuth are designed for production use and are highly optimized.\u003C/p\u003E\n\u003Cp\u003EThe differences here are about \u003Cstrong\u003Etrade-offs\u003C/strong\u003E, not absolute speed:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Even the most server-controlled mode is performant enough for real-world applications.\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022how-to-think-about-auth-modes\u0022\u003E\uD83E\uDDE0 How to Think About Auth Modes\u003C/h2\u003E\n\u003Cp\u003EIt\u2019s important to understand that authentication modes in UltimateAuth are not rigid system-wide choices.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 You are not expected to pick a single mode and use it everywhere.\u003C/p\u003E\n\u003Cp\u003EInstead:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EDifferent clients can use different modes on a single UAuthHub\u003C/li\u003E\n\u003Cli\u003EThe mode can change \u003Cstrong\u003Eper request\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EUltimateAuth selects the most appropriate mode based on \u003Cstrong\u003EClient Profile and runtime context\u003C/strong\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch3 id=\u0022runtime-driven-behavior\u0022\u003E\uD83D\uDD04 Runtime-Driven Behavior\u003C/h3\u003E\n\u003Cp\u003EIn a typical application:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EBlazor Server \u2192 PureOpaque\u003C/li\u003E\n\u003Cli\u003EBlazor WASM \u2192 Hybrid\u003C/li\u003E\n\u003Cli\u003EAPI \u2192 PureJwt\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 All can coexist in the same system.\u003C/p\u003E\n\u003Cp\u003EYou don\u2019t split your architecture \u2014 UltimateAuth adapts automatically.\u003C/p\u003E\n\u003Ch3 id=\u0022you-can-override-everything\u0022\u003E\u2699\uFE0F You Can Override Everything\u003C/h3\u003E\n\u003Cp\u003EUltimateAuth provides \u003Cstrong\u003Esafe defaults\u003C/strong\u003E, but nothing is locked.\u003C/p\u003E\n\u003Cp\u003EYou can:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EForce a specific auth mode\u003C/li\u003E\n\u003Cli\u003ECustomize behavior per client\u003C/li\u003E\n\u003Cli\u003EReplace default strategies\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 The system is designed to be flexible without sacrificing safety.\u003C/p\u003E\n\u003Ch3 id=\u0022safe-by-default\u0022\u003E\uD83D\uDEE1 Safe by Default\u003C/h3\u003E\n\u003Cp\u003EThe comparison table shows trade-offs \u2014 not risks.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAll modes are \u003Cstrong\u003Evalid and supported\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EChoosing a different mode will not \u201Cbreak\u201D security\u003C/li\u003E\n\u003Cli\u003EIncompatible configurations will \u003Cstrong\u003Efail fast\u003C/strong\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You are always operating within a safe boundary.\u003C/p\u003E\n\u003Ch3 id=\u0022mental-model\u0022\u003E\uD83D\uDCA1 Mental Model\u003C/h3\u003E\n\u003Cp\u003EThink of auth modes as:\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003EDifferent execution strategies \u2014 not different systems.\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cp\u003EUltimateAuth remains consistent.\u003Cbr /\u003E\nOnly the \u003Cstrong\u003Ebehavior adapts\u003C/strong\u003E.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022pureopaque-1\u0022\u003E\uD83D\uDD10 PureOpaque\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EFully session-based\u003C/li\u003E\n\u003Cli\u003EEvery request validated on server\u003C/li\u003E\n\u003Cli\u003EMaximum security\u003C/li\u003E\n\u003Cli\u003ETouch semantics instead of refresh rotation\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Best for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EBlazor Server\u003C/li\u003E\n\u003Cli\u003EInternal apps\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022hybrid-1\u0022\u003E\u26A1 Hybrid\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EAccess token as opaque session id\u003C/li\u003E\n\u003Cli\u003ERefresh token with rotate semantics\u003C/li\u003E\n\u003Cli\u003EServer control with API performance\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Best for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EBlazor WASM\u003C/li\u003E\n\u003Cli\u003EWeb \u002B API systems\u003C/li\u003E\n\u003Cli\u003EFull-stack apps\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022semihybrid-1\u0022\u003E\uD83D\uDE80 SemiHybrid\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EJWT-based access\u003C/li\u003E\n\u003Cli\u003EServer-side metadata control\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Best for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EHigh-performance APIs\u003C/li\u003E\n\u003Cli\u003EZero-trust systems\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022purejwt-1\u0022\u003E\uD83C\uDF10 PureJwt\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EFully stateless\u003C/li\u003E\n\u003Cli\u003ENo server-side session control\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Best for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EExternal APIs\u003C/li\u003E\n\u003Cli\u003EMicroservices\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022which-mode-should-you-use\u0022\u003E\uD83C\uDFAF Which Mode Should You Use?\u003C/h2\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EScenario\u003C/th\u003E\n\u003Cth\u003ERecommended Mode\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EBlazor Server\u003C/td\u003E\n\u003Ctd\u003EPureOpaque\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EWeb \u002B API\u003C/td\u003E\n\u003Ctd\u003EHybrid\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EHigh-scale API\u003C/td\u003E\n\u003Ctd\u003ESemiHybrid\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EExternal microservices\u003C/td\u003E\n\u003Ctd\u003EPureJwt\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n" + "Html": "\u003Cblockquote\u003E\n\u003Cp\u003ENote: SemiHybrid and PureJwt modes will be available on future releases. For now you can safely use PureOpaque and Hybrid modes.\u003C/p\u003E\n\u003C/blockquote\u003E\n\n\u003Cp\u003EUltimateAuth supports multiple authentication modes.\u003C/p\u003E\n\u003Cp\u003EEach mode represents a different balance between:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESecurity\u003C/li\u003E\n\u003Cli\u003EPerformance\u003C/li\u003E\n\u003Cli\u003EControl\u003C/li\u003E\n\u003Cli\u003EClient capabilities\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You don\u2019t always choose a single model.\u003Cbr /\u003E\nUltimateAuth can adapt based on context.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022available-modes\u0022\u003E\uD83E\uDDE9 Available Modes\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022pureopaque\u0022\u003EPureOpaque\u003C/h3\u003E\n\u003Cp\u003EFully server-controlled session model.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022hybrid\u0022\u003EHybrid\u003C/h3\u003E\n\u003Cp\u003ECombines session control with token-based access.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022semihybrid\u0022\u003ESemiHybrid\u003C/h3\u003E\n\u003Cp\u003EJWT-based with server-side metadata awareness.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022purejwt\u0022\u003EPureJwt\u003C/h3\u003E\n\u003Cp\u003EFully stateless token-based authentication.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mode-comparison\u0022\u003E\u2696\uFE0F Mode Comparison\u003C/h2\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EFeature\u003C/th\u003E\n\u003Cth\u003EPureOpaque\u003C/th\u003E\n\u003Cth\u003EHybrid\u003C/th\u003E\n\u003Cth\u003ESemiHybrid\u003C/th\u003E\n\u003Cth\u003EPureJwt\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ESessionId\u003C/td\u003E\n\u003Ctd\u003ERequired\u003C/td\u003E\n\u003Ctd\u003ERequired\u003C/td\u003E\n\u003Ctd\u003EMetadata only\u003C/td\u003E\n\u003Ctd\u003ENone\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EAccess Token\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ERefresh Token\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ERevocation\u003C/td\u003E\n\u003Ctd\u003EImmediate\u003C/td\u003E\n\u003Ctd\u003EImmediate\u003C/td\u003E\n\u003Ctd\u003EMetadata-based\u003C/td\u003E\n\u003Ctd\u003ENot immediate\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EStatefulness\u003C/td\u003E\n\u003Ctd\u003EFull\u003C/td\u003E\n\u003Ctd\u003EHybrid\u003C/td\u003E\n\u003Ctd\u003ESemi\u003C/td\u003E\n\u003Ctd\u003EStateless\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EServer Control\u003C/td\u003E\n\u003Ctd\u003EFull\u003C/td\u003E\n\u003Ctd\u003EHigh\u003C/td\u003E\n\u003Ctd\u003EMedium\u003C/td\u003E\n\u003Ctd\u003ELow\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EPerformance*\u003C/td\u003E\n\u003Ctd\u003EMedium\u003C/td\u003E\n\u003Ctd\u003EMedium\u003C/td\u003E\n\u003Ctd\u003EHigh\u003C/td\u003E\n\u003Ctd\u003EHighest\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EOffline Support\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003EPartial\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003Ctd\u003E\u2714\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003E\u26A1 \u003Cstrong\u003EPerformance Note\u003C/strong\u003E\u003C/p\u003E\n\u003Cp\u003EAll modes in UltimateAuth are designed for production use and are highly optimized.\u003C/p\u003E\n\u003Cp\u003EThe differences here are about \u003Cstrong\u003Etrade-offs\u003C/strong\u003E, not absolute speed:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Even the most server-controlled mode is performant enough for real-world applications.\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022how-to-think-about-auth-modes\u0022\u003E\uD83E\uDDE0 How to Think About Auth Modes\u003C/h2\u003E\n\u003Cp\u003EIt\u2019s important to understand that authentication modes in UltimateAuth are not rigid system-wide choices.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 You are not expected to pick a single mode and use it everywhere.\u003C/p\u003E\n\u003Cp\u003EInstead:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EDifferent clients can use different modes on a single UAuthHub\u003C/li\u003E\n\u003Cli\u003EThe mode can change \u003Cstrong\u003Eper request\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EUltimateAuth selects the most appropriate mode based on \u003Cstrong\u003EClient Profile and runtime context\u003C/strong\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022runtime-driven-behavior\u0022\u003E\uD83D\uDD04 Runtime-Driven Behavior\u003C/h3\u003E\n\u003Cp\u003EIn a typical application:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EBlazor Server \u2192 PureOpaque\u003C/li\u003E\n\u003Cli\u003EBlazor WASM \u2192 Hybrid\u003C/li\u003E\n\u003Cli\u003EAPI \u2192 PureJwt\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 All can coexist in the same system.\u003C/p\u003E\n\u003Cp\u003EYou don\u2019t split your architecture \u2014 UltimateAuth adapts automatically.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022you-can-override-everything\u0022\u003E\u2699\uFE0F You Can Override Everything\u003C/h3\u003E\n\u003Cp\u003EUltimateAuth provides \u003Cstrong\u003Esafe defaults\u003C/strong\u003E, but nothing is locked.\u003C/p\u003E\n\u003Cp\u003EYou can:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EForce a specific auth mode\u003C/li\u003E\n\u003Cli\u003ECustomize behavior per client\u003C/li\u003E\n\u003Cli\u003EReplace default strategies\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 The system is designed to be flexible without sacrificing safety.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022safe-by-default\u0022\u003E\uD83D\uDEE1 Safe by Default\u003C/h3\u003E\n\u003Cp\u003EThe comparison table shows trade-offs \u2014 not risks.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAll modes are \u003Cstrong\u003Evalid and supported\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EChoosing a different mode will not \u201Cbreak\u201D security\u003C/li\u003E\n\u003Cli\u003EIncompatible configurations will \u003Cstrong\u003Efail fast\u003C/strong\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You are always operating within a safe boundary.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83D\uDCA1 Mental Model\u003C/h3\u003E\n\u003Cp\u003EThink of auth modes as:\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003EDifferent execution strategies \u2014 not different systems.\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cp\u003EUltimateAuth remains consistent.\u003Cbr /\u003E\nOnly the \u003Cstrong\u003Ebehavior adapts\u003C/strong\u003E.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022pureopaque-1\u0022\u003E\uD83D\uDD10 PureOpaque\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EFully session-based\u003C/li\u003E\n\u003Cli\u003EEvery request validated on server\u003C/li\u003E\n\u003Cli\u003EMaximum security\u003C/li\u003E\n\u003Cli\u003ETouch semantics instead of refresh rotation\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Best for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EBlazor Server\u003C/li\u003E\n\u003Cli\u003EInternal apps\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022hybrid-1\u0022\u003E\u26A1 Hybrid\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EAccess token as opaque session id\u003C/li\u003E\n\u003Cli\u003ERefresh token with rotate semantics\u003C/li\u003E\n\u003Cli\u003EServer control with API performance\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Best for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EBlazor WASM\u003C/li\u003E\n\u003Cli\u003EWeb \u002B API systems\u003C/li\u003E\n\u003Cli\u003EFull-stack apps\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022semihybrid-1\u0022\u003E\uD83D\uDE80 SemiHybrid\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EJWT-based access\u003C/li\u003E\n\u003Cli\u003EServer-side metadata control\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Best for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EHigh-performance APIs\u003C/li\u003E\n\u003Cli\u003EZero-trust systems\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022purejwt-1\u0022\u003E\uD83C\uDF10 PureJwt\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EFully stateless\u003C/li\u003E\n\u003Cli\u003ENo server-side session control\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Best for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EExternal APIs\u003C/li\u003E\n\u003Cli\u003EMicroservices\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022which-mode-should-you-use\u0022\u003E\uD83C\uDFAF Which Mode Should You Use?\u003C/h2\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EScenario\u003C/th\u003E\n\u003Cth\u003ERecommended Mode\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EBlazor Server\u003C/td\u003E\n\u003Ctd\u003EPureOpaque\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EWeb \u002B API\u003C/td\u003E\n\u003Ctd\u003EHybrid\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EHigh-scale API\u003C/td\u003E\n\u003Ctd\u003ESemiHybrid\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EExternal microservices\u003C/td\u003E\n\u003Ctd\u003EPureJwt\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n", + "Headings": [ + { + "Id": "available-modes", + "Text": "\uD83E\uDDE9 Available Modes", + "Level": 0 + }, + { + "Id": "pureopaque", + "Text": "PureOpaque", + "Level": 1 + }, + { + "Id": "hybrid", + "Text": "Hybrid", + "Level": 1 + }, + { + "Id": "semihybrid", + "Text": "SemiHybrid", + "Level": 1 + }, + { + "Id": "purejwt", + "Text": "PureJwt", + "Level": 1 + }, + { + "Id": "mode-comparison", + "Text": "\u2696\uFE0F Mode Comparison", + "Level": 0 + }, + { + "Id": "how-to-think-about-auth-modes", + "Text": "\uD83E\uDDE0 How to Think About Auth Modes", + "Level": 0 + }, + { + "Id": "runtime-driven-behavior", + "Text": "\uD83D\uDD04 Runtime-Driven Behavior", + "Level": 1 + }, + { + "Id": "you-can-override-everything", + "Text": "\u2699\uFE0F You Can Override Everything", + "Level": 1 + }, + { + "Id": "safe-by-default", + "Text": "\uD83D\uDEE1 Safe by Default", + "Level": 1 + }, + { + "Id": "mental-model", + "Text": "\uD83D\uDCA1 Mental Model", + "Level": 1 + }, + { + "Id": "pureopaque-1", + "Text": "\uD83D\uDD10 PureOpaque", + "Level": 0 + }, + { + "Id": "hybrid-1", + "Text": "\u26A1 Hybrid", + "Level": 0 + }, + { + "Id": "semihybrid-1", + "Text": "\uD83D\uDE80 SemiHybrid", + "Level": 0 + }, + { + "Id": "purejwt-1", + "Text": "\uD83C\uDF10 PureJwt", + "Level": 0 + }, + { + "Id": "which-mode-should-you-use", + "Text": "\uD83C\uDFAF Which Mode Should You Use?", + "Level": 0 + } + ] } \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/client-profiles.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/client-profiles.json index ccaf4764..101ce23b 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/client-profiles.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/client-profiles.json @@ -1,5 +1,77 @@ { "Slug": "fundamentals/client-profiles", "Title": "Client Profiles", - "Html": "\n\u003Cp\u003EUltimateAuth adapts its authentication behavior based on the client.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 This is powered by \u003Cstrong\u003EClient Profiles\u003C/strong\u003E.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022what-is-a-client-profile\u0022\u003E\uD83D\uDD11 What is a Client Profile?\u003C/h2\u003E\n\u003Cp\u003EA Client Profile defines how authentication behaves for a specific client type.\u003C/p\u003E\n\u003Cp\u003EIt affects:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAuthentication mode\u003C/li\u003E\n\u003Cli\u003EFlow behavior\u003C/li\u003E\n\u003Cli\u003EToken usage\u003C/li\u003E\n\u003Cli\u003ESecurity constraints\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EUnlike traditional systems:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 You don\u2019t configure authentication globally\u003Cbr /\u003E\n\uD83D\uDC49 You let the system adapt per client\u003C/p\u003E\n\u003Ch2 id=\u0022the-key-idea\u0022\u003E\uD83E\uDDE0 The Key Idea\u003C/h2\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003EAuthentication behavior is not static\u003Cbr /\u003E\nIt is determined \u003Cstrong\u003Eper client and per request\u003C/strong\u003E\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022runtime-detection\u0022\u003E\uD83D\uDD04 Runtime Detection\u003C/h2\u003E\n\u003Cp\u003EBy default, UltimateAuth automatically detects the client profile.\u003C/p\u003E\n\u003Cp\u003EThis is done using runtime signals such as:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELoaded assemblies\u003C/li\u003E\n\u003Cli\u003EHosting environment\u003C/li\u003E\n\u003Cli\u003ERegistered services\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022example-detection-logic\u0022\u003EExample Detection Logic\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EMAUI assemblies \u2192 \u003Ccode\u003EMaui\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003EWebAssembly runtime \u2192 \u003Ccode\u003EBlazorWasm\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003EServer components \u2192 \u003Ccode\u003EBlazorServer\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003EUAuthHub marker \u2192 \u003Ccode\u003EUAuthHub\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003EFallback \u2192 \u003Ccode\u003EWebServer\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Detection happens inside the client at startup.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022client-server-propagation\u0022\u003E\uD83D\uDCE1 Client \u2192 Server Propagation\u003C/h2\u003E\n\u003Cp\u003EThe detected client profile is sent to the server on each request.\u003C/p\u003E\n\u003Cp\u003EThis can happen via:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERequest headers\u003C/li\u003E\n\u003Cli\u003EForm fields (for flow-based operations)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EClient \u2192 (ClientProfile) \u2192 Server\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EOn the server:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EThe profile is read from the request\u003C/li\u003E\n\u003Cli\u003EIf not provided \u2192 NotSpecified\u003C/li\u003E\n\u003Cli\u003EServer applies defaults or resolves behavior\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022automatic-vs-manual-configuration\u0022\u003E\u2699\uFE0F Automatic vs Manual Configuration\u003C/h2\u003E\n\u003Ch3 id=\u0022automatic-default\u0022\u003EAutomatic (Default)\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthClientBlazor();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EIt means:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAutoDetectClientProfile = true\u003C/li\u003E\n\u003Cli\u003EProfile is resolved automatically\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022manual-override\u0022\u003EManual Override\u003C/h3\u003E\n\u003Cp\u003EYou can explicitly set the client profile:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthClientBlazor(o =\u0026gt;\n{\n o.ClientProfile = UAuthClientProfile.Maui;\n o.AutoDetectClientProfile = false; // optional\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This is useful when:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERunning in custom hosting environments\u003C/li\u003E\n\u003Cli\u003EDetection is ambiguous\u003C/li\u003E\n\u003Cli\u003EYou want full control\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022built-in-profiles\u0022\u003E\uD83E\uDDE9 Built-in Profiles\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth includes predefined profiles:\u003C/p\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EProfile\u003C/th\u003E\n\u003Cth\u003EDescription\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EBlazorServer\u003C/td\u003E\n\u003Ctd\u003EServer-rendered apps\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EBlazorWasm\u003C/td\u003E\n\u003Ctd\u003EBrowser-based WASM apps\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EMaui\u003C/td\u003E\n\u003Ctd\u003ENative mobile apps\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EWebServer\u003C/td\u003E\n\u003Ctd\u003EMVC / Razor / generic server\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EApi\u003C/td\u003E\n\u003Ctd\u003EAPI-only backend\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EUAuthHub\u003C/td\u003E\n\u003Ctd\u003ECentral auth server\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Ch3 id=\u0022safe-defaults\u0022\u003E\uD83D\uDEE1 Safe Defaults\u003C/h3\u003E\n\u003Cp\u003EIf no profile is specified (and auto detection is false):\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EClient \u2192 NotSpecified \u2192 Server resolves safely\u003C/li\u003E\n\u003Cli\u003EDefaults are applied\u003C/li\u003E\n\u003Cli\u003EUnsafe combinations are prevented\u003C/li\u003E\n\u003Cli\u003ESystem remains consistent\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022why-this-matters\u0022\u003E\uD83D\uDD10 Why This Matters\u003C/h3\u003E\n\u003Cp\u003EClient Profiles enable:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EMulti-client systems (Web \u002B Mobile \u002B API)\u003C/li\u003E\n\u003Cli\u003ERuntime adaptation\u003C/li\u003E\n\u003Cli\u003ESafe defaults without manual configuration\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EWithout Client Profiles You would need:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESeparate auth setups per client\u003C/li\u003E\n\u003Cli\u003EComplex branching logic\u003C/li\u003E\n\u003Cli\u003EManual security handling\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Client defines behavior\n\uD83D\uDC49 Server enforces rules\u003C/p\u003E\n\u003Ch2 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EClient Profiles are automatically detected\u003C/li\u003E\n\u003Cli\u003EThey are sent to the server on each request\u003C/li\u003E\n\u003Cli\u003EBehavior adapts per request\u003C/li\u003E\n\u003Cli\u003EYou can override everything when needed\u003C/li\u003E\n\u003Cli\u003ESafe defaults are always applied\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003ENow that you understand runtime behavior:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Continue to Runtime Architecture\u003C/p\u003E\n" + "Html": "\n\u003Cp\u003EUltimateAuth adapts its authentication behavior based on the client.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 This is powered by \u003Cstrong\u003EClient Profiles\u003C/strong\u003E.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-is-a-client-profile\u0022\u003E\uD83D\uDD11 What is a Client Profile?\u003C/h2\u003E\n\u003Cp\u003EA Client Profile defines how authentication behaves for a specific client type.\u003C/p\u003E\n\u003Cp\u003EIt affects:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAuthentication mode\u003C/li\u003E\n\u003Cli\u003EFlow behavior\u003C/li\u003E\n\u003Cli\u003EToken usage\u003C/li\u003E\n\u003Cli\u003ESecurity constraints\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EUnlike traditional systems:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 You don\u2019t configure authentication globally\u003Cbr /\u003E\n\uD83D\uDC49 You let the system adapt per client\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022the-key-idea\u0022\u003E\uD83E\uDDE0 The Key Idea\u003C/h2\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003EAuthentication behavior is not static\u003Cbr /\u003E\nIt is determined \u003Cstrong\u003Eper client and per request\u003C/strong\u003E\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022runtime-detection\u0022\u003E\uD83D\uDD04 Runtime Detection\u003C/h2\u003E\n\u003Cp\u003EBy default, UltimateAuth automatically detects the client profile.\u003C/p\u003E\n\u003Cp\u003EThis is done using runtime signals such as:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELoaded assemblies\u003C/li\u003E\n\u003Cli\u003EHosting environment\u003C/li\u003E\n\u003Cli\u003ERegistered services\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022example-detection-logic\u0022\u003EExample Detection Logic\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EMAUI assemblies \u2192 \u003Ccode\u003EMaui\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003EWebAssembly runtime \u2192 \u003Ccode\u003EBlazorWasm\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003EServer components \u2192 \u003Ccode\u003EBlazorServer\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003EUAuthHub marker \u2192 \u003Ccode\u003EUAuthHub\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003EFallback \u2192 \u003Ccode\u003EWebServer\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Detection happens inside the client at startup.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022client-server-propagation\u0022\u003E\uD83D\uDCE1 Client \u2192 Server Propagation\u003C/h2\u003E\n\u003Cp\u003EThe detected client profile is sent to the server on each request.\u003C/p\u003E\n\u003Cp\u003EThis can happen via:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERequest headers\u003C/li\u003E\n\u003Cli\u003EForm fields (for flow-based operations)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EClient \u2192 (ClientProfile) \u2192 Server\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EOn the server:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EThe profile is read from the request\u003C/li\u003E\n\u003Cli\u003EIf not provided \u2192 NotSpecified\u003C/li\u003E\n\u003Cli\u003EServer applies defaults or resolves behavior\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022automatic-vs-manual-configuration\u0022\u003E\u2699\uFE0F Automatic vs Manual Configuration\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022automatic-default\u0022\u003EAutomatic (Default)\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthClientBlazor();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EIt means:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAutoDetectClientProfile = true\u003C/li\u003E\n\u003Cli\u003EProfile is resolved automatically\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022manual-override\u0022\u003EManual Override\u003C/h3\u003E\n\u003Cp\u003EYou can explicitly set the client profile:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthClientBlazor(o =\u0026gt;\n{\n o.ClientProfile = UAuthClientProfile.Maui;\n o.AutoDetectClientProfile = false; // optional\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This is useful when:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERunning in custom hosting environments\u003C/li\u003E\n\u003Cli\u003EDetection is ambiguous\u003C/li\u003E\n\u003Cli\u003EYou want full control\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022built-in-profiles\u0022\u003E\uD83E\uDDE9 Built-in Profiles\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth includes predefined profiles:\u003C/p\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EProfile\u003C/th\u003E\n\u003Cth\u003EDescription\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EBlazorServer\u003C/td\u003E\n\u003Ctd\u003EServer-rendered apps\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EBlazorWasm\u003C/td\u003E\n\u003Ctd\u003EBrowser-based WASM apps\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EMaui\u003C/td\u003E\n\u003Ctd\u003ENative mobile apps\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EWebServer\u003C/td\u003E\n\u003Ctd\u003EMVC / Razor / generic server\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EApi\u003C/td\u003E\n\u003Ctd\u003EAPI-only backend\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EUAuthHub\u003C/td\u003E\n\u003Ctd\u003ECentral auth server\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022safe-defaults\u0022\u003E\uD83D\uDEE1 Safe Defaults\u003C/h3\u003E\n\u003Cp\u003EIf no profile is specified (and auto detection is false):\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EClient \u2192 NotSpecified \u2192 Server resolves safely\u003C/li\u003E\n\u003Cli\u003EDefaults are applied\u003C/li\u003E\n\u003Cli\u003EUnsafe combinations are prevented\u003C/li\u003E\n\u003Cli\u003ESystem remains consistent\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022why-this-matters\u0022\u003E\uD83D\uDD10 Why This Matters\u003C/h3\u003E\n\u003Cp\u003EClient Profiles enable:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EMulti-client systems (Web \u002B Mobile \u002B API)\u003C/li\u003E\n\u003Cli\u003ERuntime adaptation\u003C/li\u003E\n\u003Cli\u003ESafe defaults without manual configuration\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EWithout Client Profiles You would need:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESeparate auth setups per client\u003C/li\u003E\n\u003Cli\u003EComplex branching logic\u003C/li\u003E\n\u003Cli\u003EManual security handling\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Client defines behavior\n\uD83D\uDC49 Server enforces rules\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EClient Profiles are automatically detected\u003C/li\u003E\n\u003Cli\u003EThey are sent to the server on each request\u003C/li\u003E\n\u003Cli\u003EBehavior adapts per request\u003C/li\u003E\n\u003Cli\u003EYou can override everything when needed\u003C/li\u003E\n\u003Cli\u003ESafe defaults are always applied\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003ENow that you understand runtime behavior:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Continue to Runtime Architecture\u003C/p\u003E\n", + "Headings": [ + { + "Id": "what-is-a-client-profile", + "Text": "\uD83D\uDD11 What is a Client Profile?", + "Level": 0 + }, + { + "Id": "the-key-idea", + "Text": "\uD83E\uDDE0 The Key Idea", + "Level": 0 + }, + { + "Id": "runtime-detection", + "Text": "\uD83D\uDD04 Runtime Detection", + "Level": 0 + }, + { + "Id": "example-detection-logic", + "Text": "Example Detection Logic", + "Level": 1 + }, + { + "Id": "client-server-propagation", + "Text": "\uD83D\uDCE1 Client \u2192 Server Propagation", + "Level": 0 + }, + { + "Id": "automatic-vs-manual-configuration", + "Text": "\u2699\uFE0F Automatic vs Manual Configuration", + "Level": 0 + }, + { + "Id": "automatic-default", + "Text": "Automatic (Default)", + "Level": 1 + }, + { + "Id": "manual-override", + "Text": "Manual Override", + "Level": 1 + }, + { + "Id": "built-in-profiles", + "Text": "\uD83E\uDDE9 Built-in Profiles", + "Level": 0 + }, + { + "Id": "safe-defaults", + "Text": "\uD83D\uDEE1 Safe Defaults", + "Level": 1 + }, + { + "Id": "why-this-matters", + "Text": "\uD83D\uDD10 Why This Matters", + "Level": 1 + }, + { + "Id": "mental-model", + "Text": "\uD83E\uDDE0 Mental Model", + "Level": 0 + }, + { + "Id": "key-takeaways", + "Text": "\uD83D\uDCCC Key Takeaways", + "Level": 0 + }, + { + "Id": "next-step", + "Text": "\u27A1\uFE0F Next Step", + "Level": 0 + } + ] } \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/flow-based-auth.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/flow-based-auth.json index f9b15a79..1d44015a 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/flow-based-auth.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/flow-based-auth.json @@ -1,5 +1,97 @@ { "Slug": "fundamentals/flow-based-auth", "Title": "Flow Based Authentication", - "Html": "\n\u003Cp\u003EUltimateAuth is not cookie-based or token-based.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 It is \u003Cstrong\u003Eflow-based\u003C/strong\u003E.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022what-does-flow-based-mean\u0022\u003E\uD83D\uDD11 What Does \u201CFlow-Based\u201D Mean?\u003C/h2\u003E\n\u003Cp\u003EIn traditional systems, authentication is treated as:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EA cookie\u003C/li\u003E\n\u003Cli\u003EA JWT token\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EOnce issued, the system simply checks:\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003E\u201CIs this token valid?\u201D\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cbr\u003E\n\u003Cp\u003EUltimateAuth takes a different approach:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Authentication is a \u003Cstrong\u003Eseries of controlled flows\u003C/strong\u003E, not a static artifact.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022authentication-as-flows\u0022\u003E\uD83E\uDDED Authentication as Flows\u003C/h2\u003E\n\u003Cp\u003EEvery authentication operation is an explicit flow:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Cstrong\u003ELogin\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003ELogout\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003EValidate\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003ERefresh\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003ERe-authentication\u003C/strong\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EEach flow:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EIs initiated intentionally\u003C/li\u003E\n\u003Cli\u003EIs processed on the server\u003C/li\u003E\n\u003Cli\u003EProduces a controlled result\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022example-login-flow\u0022\u003E\uD83D\uDD01 Example: Login Flow\u003C/h2\u003E\n\u003Cp\u003EInstead of:\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003E\u201CGenerate a token and store it\u201D\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cp\u003EUltimateAuth does:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003ELogin Request\n\u2192 Validate credentials\n\u2192 Resolve Root\n\u2192 Resolve or create Chain\n\u2192 Create Session\n\u2192 Issue authentication grant\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Login is not a single step \u2014 it is a \u003Cstrong\u003Emanaged process\u003C/strong\u003E\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022example-refresh-flow\u0022\u003E\uD83D\uDD04 Example: Refresh Flow\u003C/h2\u003E\n\u003Cp\u003ETraditional systems:\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003ERefresh = issue new token\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cp\u003EUltimateAuth:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003ERefresh Request\n\u2192 Validate session\n\u2192 Check security constraints\n\u2192 Apply policies (if any)\n\u2192 Optionally rotate tokens\n\u2192 Update session state (if needed)\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 The server decides what actually happens\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022example-validate-flow\u0022\u003E\uD83D\uDD0D Example: Validate Flow\u003C/h2\u003E\n\u003Cp\u003EOn each request:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003EIncoming Request\n\u2192 Extract session/token\n\u2192 Validate session\n\u2192 Check chain (device context)\n\u2192 Verify root security version\n\u2192 Build auth state\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Validation is not just \u201Ctoken valid?\u201D\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022why-token-based-thinking-falls-short\u0022\u003E\u26A0\uFE0F Why Token-Based Thinking Falls Short\u003C/h2\u003E\n\u003Cp\u003EToken-based systems assume:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EThe token contains truth\u003C/li\u003E\n\u003Cli\u003EThe server trusts the token\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThis leads to:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EWeak revocation\u003C/li\u003E\n\u003Cli\u003ENo device awareness\u003C/li\u003E\n\u003Cli\u003ELimited control\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022ultimateauth-approach\u0022\u003E\u2705 UltimateAuth Approach\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth treats tokens as:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Cstrong\u003Etransport artifacts\u003C/strong\u003E, not sources of truth\u003C/p\u003E\n\u003Cp\u003EThe real authority is:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERoot\u003C/li\u003E\n\u003Cli\u003EChain\u003C/li\u003E\n\u003Cli\u003ESession\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022key-idea\u0022\u003E\uD83E\uDDE0 Key Idea\u003C/h2\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003ETokens carry data\u003Cbr /\u003E\nFlows enforce rules\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022server-controlled-by-design\u0022\u003E\uD83D\uDD10 Server-Controlled by Design\u003C/h2\u003E\n\u003Cp\u003EAll flows are:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EExecuted on the server\u003C/li\u003E\n\u003Cli\u003EEvaluated against policies\u003C/li\u003E\n\u003Cli\u003ESubject to security constraints\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 The client does not control authentication state\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022flow-examples-in-code\u0022\u003E\u2699\uFE0F Flow Examples in Code\u003C/h2\u003E\n\u003Cp\u003EUsing \u003Ccode\u003EIUAuthClient\u003C/code\u003E:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Flows.LoginAsync(request);\nawait UAuthClient.Flows.RefreshAsync();\nawait UAuthClient.Flows.LogoutAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Each method represents a server-driven flow\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022how-this-changes-development\u0022\u003E\uD83E\uDDE9 How This Changes Development\u003C/h2\u003E\n\u003Cp\u003EInstead of thinking:\u003C/p\u003E\n\u003Cp\u003E\u274C \u201CI need to manage tokens\u201D\u003C/p\u003E\n\u003Cp\u003EYou think:\u003C/p\u003E\n\u003Cp\u003E\u2705 \u201CI need to trigger flows\u201D\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022benefits-of-flow-based-authentication\u0022\u003E\uD83D\uDCCC Benefits of Flow-Based Authentication\u003C/h2\u003E\n\u003Ch3 id=\u0022predictable-behavior\u0022\u003E\u2714 Predictable Behavior\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EEvery action is explicit and controlled.\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022better-security\u0022\u003E\u2714 Better Security\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ENo blind token trust\u003C/li\u003E\n\u003Cli\u003EServer-side validation\u003C/li\u003E\n\u003Cli\u003EPolicy-driven decisions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022extensibility\u0022\u003E\u2714 Extensibility\u003C/h3\u003E\n\u003Cp\u003EFlows can be extended with:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EMFA\u003C/li\u003E\n\u003Cli\u003ERisk-based checks\u003C/li\u003E\n\u003Cli\u003ECustom policies\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022consistent-across-clients\u0022\u003E\u2714 Consistent Across Clients\u003C/h3\u003E\n\u003Cp\u003ESame flows work for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EBlazor Server\u003C/li\u003E\n\u003Cli\u003EWASM (PKCE)\u003C/li\u003E\n\u003Cli\u003EAPIs\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Authentication is not a token \u2014 it is a process\u003C/p\u003E\n\u003Ch2 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003ENow that you understand flows:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Continue to Auth Modes\u003C/p\u003E\n" + "Html": "\n\u003Cp\u003EUltimateAuth is not cookie-based or token-based.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 It is \u003Cstrong\u003Eflow-based\u003C/strong\u003E.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-does-flow-based-mean\u0022\u003E\uD83D\uDD11 What Does \u201CFlow-Based\u201D Mean?\u003C/h2\u003E\n\u003Cp\u003EIn traditional systems, authentication is treated as:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EA cookie\u003C/li\u003E\n\u003Cli\u003EA JWT token\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EOnce issued, the system simply checks:\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003E\u201CIs this token valid?\u201D\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cbr\u003E\n\u003Cp\u003EUltimateAuth takes a different approach:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Authentication is a \u003Cstrong\u003Eseries of controlled flows\u003C/strong\u003E, not a static artifact.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022authentication-as-flows\u0022\u003E\uD83E\uDDED Authentication as Flows\u003C/h2\u003E\n\u003Cp\u003EEvery authentication operation is an explicit flow:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Cstrong\u003ELogin\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003ELogout\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003EValidate\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003ERefresh\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Cstrong\u003ERe-authentication\u003C/strong\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EEach flow:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EIs initiated intentionally\u003C/li\u003E\n\u003Cli\u003EIs processed on the server\u003C/li\u003E\n\u003Cli\u003EProduces a controlled result\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022example-login-flow\u0022\u003E\uD83D\uDD01 Example: Login Flow\u003C/h2\u003E\n\u003Cp\u003EInstead of:\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003E\u201CGenerate a token and store it\u201D\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cp\u003EUltimateAuth does:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003ELogin Request\n\u2192 Validate credentials\n\u2192 Resolve Root\n\u2192 Resolve or create Chain\n\u2192 Create Session\n\u2192 Issue authentication grant\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Login is not a single step \u2014 it is a \u003Cstrong\u003Emanaged process\u003C/strong\u003E\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022example-refresh-flow\u0022\u003E\uD83D\uDD04 Example: Refresh Flow\u003C/h2\u003E\n\u003Cp\u003ETraditional systems:\u003C/p\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003ERefresh = issue new token\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cp\u003EUltimateAuth:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003ERefresh Request\n\u2192 Validate session\n\u2192 Check security constraints\n\u2192 Apply policies (if any)\n\u2192 Optionally rotate tokens\n\u2192 Update session state (if needed)\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 The server decides what actually happens\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022example-validate-flow\u0022\u003E\uD83D\uDD0D Example: Validate Flow\u003C/h2\u003E\n\u003Cp\u003EOn each request:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003EIncoming Request\n\u2192 Extract session/token\n\u2192 Validate session\n\u2192 Check chain (device context)\n\u2192 Verify root security version\n\u2192 Build auth state\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Validation is not just \u201Ctoken valid?\u201D\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022why-token-based-thinking-falls-short\u0022\u003E\u26A0\uFE0F Why Token-Based Thinking Falls Short\u003C/h2\u003E\n\u003Cp\u003EToken-based systems assume:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EThe token contains truth\u003C/li\u003E\n\u003Cli\u003EThe server trusts the token\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThis leads to:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EWeak revocation\u003C/li\u003E\n\u003Cli\u003ENo device awareness\u003C/li\u003E\n\u003Cli\u003ELimited control\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022ultimateauth-approach\u0022\u003E\u2705 UltimateAuth Approach\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth treats tokens as:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Cstrong\u003Etransport artifacts\u003C/strong\u003E, not sources of truth\u003C/p\u003E\n\u003Cp\u003EThe real authority is:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERoot\u003C/li\u003E\n\u003Cli\u003EChain\u003C/li\u003E\n\u003Cli\u003ESession\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022key-idea\u0022\u003E\uD83E\uDDE0 Key Idea\u003C/h2\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003ETokens carry data\u003Cbr /\u003E\nFlows enforce rules\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022server-controlled-by-design\u0022\u003E\uD83D\uDD10 Server-Controlled by Design\u003C/h2\u003E\n\u003Cp\u003EAll flows are:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EExecuted on the server\u003C/li\u003E\n\u003Cli\u003EEvaluated against policies\u003C/li\u003E\n\u003Cli\u003ESubject to security constraints\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 The client does not control authentication state\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022flow-examples-in-code\u0022\u003E\u2699\uFE0F Flow Examples in Code\u003C/h2\u003E\n\u003Cp\u003EUsing \u003Ccode\u003EIUAuthClient\u003C/code\u003E:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eawait UAuthClient.Flows.LoginAsync(request);\nawait UAuthClient.Flows.RefreshAsync();\nawait UAuthClient.Flows.LogoutAsync();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Each method represents a server-driven flow\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022how-this-changes-development\u0022\u003E\uD83E\uDDE9 How This Changes Development\u003C/h2\u003E\n\u003Cp\u003EInstead of thinking:\u003C/p\u003E\n\u003Cp\u003E\u274C \u201CI need to manage tokens\u201D\u003C/p\u003E\n\u003Cp\u003EYou think:\u003C/p\u003E\n\u003Cp\u003E\u2705 \u201CI need to trigger flows\u201D\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022benefits-of-flow-based-authentication\u0022\u003E\uD83D\uDCCC Benefits of Flow-Based Authentication\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022predictable-behavior\u0022\u003E\u2714 Predictable Behavior\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EEvery action is explicit and controlled.\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022better-security\u0022\u003E\u2714 Better Security\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ENo blind token trust\u003C/li\u003E\n\u003Cli\u003EServer-side validation\u003C/li\u003E\n\u003Cli\u003EPolicy-driven decisions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022extensibility\u0022\u003E\u2714 Extensibility\u003C/h3\u003E\n\u003Cp\u003EFlows can be extended with:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EMFA\u003C/li\u003E\n\u003Cli\u003ERisk-based checks\u003C/li\u003E\n\u003Cli\u003ECustom policies\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022consistent-across-clients\u0022\u003E\u2714 Consistent Across Clients\u003C/h3\u003E\n\u003Cp\u003ESame flows work for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EBlazor Server\u003C/li\u003E\n\u003Cli\u003EWASM (PKCE)\u003C/li\u003E\n\u003Cli\u003EAPIs\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Authentication is not a token \u2014 it is a process\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003ENow that you understand flows:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Continue to Auth Modes\u003C/p\u003E\n", + "Headings": [ + { + "Id": "what-does-flow-based-mean", + "Text": "\uD83D\uDD11 What Does \u201CFlow-Based\u201D Mean?", + "Level": 0 + }, + { + "Id": "authentication-as-flows", + "Text": "\uD83E\uDDED Authentication as Flows", + "Level": 0 + }, + { + "Id": "example-login-flow", + "Text": "\uD83D\uDD01 Example: Login Flow", + "Level": 0 + }, + { + "Id": "example-refresh-flow", + "Text": "\uD83D\uDD04 Example: Refresh Flow", + "Level": 0 + }, + { + "Id": "example-validate-flow", + "Text": "\uD83D\uDD0D Example: Validate Flow", + "Level": 0 + }, + { + "Id": "why-token-based-thinking-falls-short", + "Text": "\u26A0\uFE0F Why Token-Based Thinking Falls Short", + "Level": 0 + }, + { + "Id": "ultimateauth-approach", + "Text": "\u2705 UltimateAuth Approach", + "Level": 0 + }, + { + "Id": "key-idea", + "Text": "\uD83E\uDDE0 Key Idea", + "Level": 0 + }, + { + "Id": "server-controlled-by-design", + "Text": "\uD83D\uDD10 Server-Controlled by Design", + "Level": 0 + }, + { + "Id": "flow-examples-in-code", + "Text": "\u2699\uFE0F Flow Examples in Code", + "Level": 0 + }, + { + "Id": "how-this-changes-development", + "Text": "\uD83E\uDDE9 How This Changes Development", + "Level": 0 + }, + { + "Id": "benefits-of-flow-based-authentication", + "Text": "\uD83D\uDCCC Benefits of Flow-Based Authentication", + "Level": 0 + }, + { + "Id": "predictable-behavior", + "Text": "\u2714 Predictable Behavior", + "Level": 1 + }, + { + "Id": "better-security", + "Text": "\u2714 Better Security", + "Level": 1 + }, + { + "Id": "extensibility", + "Text": "\u2714 Extensibility", + "Level": 1 + }, + { + "Id": "consistent-across-clients", + "Text": "\u2714 Consistent Across Clients", + "Level": 1 + }, + { + "Id": "mental-model", + "Text": "\uD83E\uDDE0 Mental Model", + "Level": 0 + }, + { + "Id": "next-step", + "Text": "\u27A1\uFE0F Next Step", + "Level": 0 + } + ] } \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/index.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/index.json index e09e0b9e..d973d5eb 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/index.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/index.json @@ -1,5 +1,6 @@ { "Slug": "fundamentals/index", "Title": "index", - "Html": "\u003Cp\u003E---\u003C/p\u003E\n\u003Cp\u003Etitle: Fundamental Overview\u003C/p\u003E\n\u003Cp\u003Eorder: 1\u003C/p\u003E\n\u003Cp\u003Egroup: fundamentals\u003C/p\u003E\n\u003Cp\u003E---\u003C/p\u003E\n\u003Cp\u003E# \uD83E\uDDE0 Fundamentals\u003C/p\u003E\n\u003Cp\u003EThis section explains how UltimateAuth works internally.\u003C/p\u003E\n\u003Cp\u003EIf you are new, follow this order:\u003C/p\u003E\n\u003Cp\u003E- Authentication Model\u003C/p\u003E\n\u003Cp\u003E- Flow-Based Authentication\u003C/p\u003E\n\u003Cp\u003E- Authentication Modes\u003C/p\u003E\n\u003Cp\u003E- Client Profiles\u003C/p\u003E\n\u003Cp\u003E- Runtime Architecture\u003C/p\u003E\n\u003Cp\u003E- Request Lifecycle\u003C/p\u003E\n" + "Html": "\u003Cp\u003E---\u003C/p\u003E\n\u003Cp\u003Etitle: Fundamental Overview\u003C/p\u003E\n\u003Cp\u003Eorder: 1\u003C/p\u003E\n\u003Cp\u003Egroup: fundamentals\u003C/p\u003E\n\u003Cp\u003E---\u003C/p\u003E\n\u003Cp\u003E# \uD83E\uDDE0 Fundamental Overview\u003C/p\u003E\n\u003Cp\u003EThis section explains how UltimateAuth works internally.\u003C/p\u003E\n\u003Cp\u003E## Section Order\u003C/p\u003E\n\u003Cp\u003EIf you are new, follow this order:\u003C/p\u003E\n\u003Cp\u003E- Authentication Model\u003C/p\u003E\n\u003Cp\u003E- Flow-Based Authentication\u003C/p\u003E\n\u003Cp\u003E- Authentication Modes\u003C/p\u003E\n\u003Cp\u003E- Client Profiles\u003C/p\u003E\n\u003Cp\u003E- Runtime Architecture\u003C/p\u003E\n\u003Cp\u003E- Request Lifecycle\u003C/p\u003E\n", + "Headings": [] } \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/request-lifecycle.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/request-lifecycle.json index 548b498a..23ab07ac 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/request-lifecycle.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/request-lifecycle.json @@ -1,5 +1,97 @@ { "Slug": "fundamentals/request-lifecycle", "Title": "Request Lifecycle", - "Html": "\n\u003Cp\u003EThis section explains what happens when a request enters UltimateAuth.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 From the moment an HTTP request arrives\u003Cbr /\u003E\n\uD83D\uDC49 Until authentication state is established or a flow is executed\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022two-types-of-requests\u0022\u003E\uD83E\uDDE0 Two Types of Requests\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth processes requests in two different ways:\u003C/p\u003E\n\u003Ch3 id=\u0022passive-requests\u0022\u003E1. Passive Requests\u003C/h3\u003E\n\u003Cp\u003ERegular application requests (page load, API call)\u003C/p\u003E\n\u003Ch3 id=\u0022active-flow-requests\u0022\u003E2. Active Flow Requests\u003C/h3\u003E\n\u003Cp\u003EAuthentication flows (login, refresh, logout)\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Both share the same foundation, but diverge at the flow level.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022middleware-pipeline\u0022\u003E\uD83E\uDDE9 Middleware Pipeline\u003C/h2\u003E\n\u003Cp\u003EEvery request passes through the UltimateAuth middleware pipeline:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003ETenant \u2192 Session Resolution \u2192 (Validation) \u2192 User Resolution\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022tenant-resolution\u0022\u003E\uD83C\uDFE2 Tenant Resolution\u003C/h3\u003E\n\u003Cp\u003EThe system determines the tenant:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EMulti-tenant \u2192 resolved via \u003Ccode\u003EITenantResolver\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003ESingle-tenant \u2192 default context applied\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 If tenant cannot be resolved \u2192 request fails early\u003C/p\u003E\n\u003Ch3 id=\u0022session-resolution\u0022\u003E\uD83D\uDD10 Session Resolution\u003C/h3\u003E\n\u003Cp\u003EThe system attempts to extract a session:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EFrom headers, cookies, or tokens\u003C/li\u003E\n\u003Cli\u003EConverted into a \u003Ccode\u003ESessionContext\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cpre\u003E\u003Ccode\u003ESessionId \u2192 SessionContext\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 If no session is found \u2192 request becomes anonymous\u003C/p\u003E\n\u003Ch3 id=\u0022session-validation-resource-apis\u0022\u003E\u2714 Session Validation (Resource APIs)\u003C/h3\u003E\n\u003Cp\u003EFor API scenarios:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession is validated immediately\u003C/li\u003E\n\u003Cli\u003EDevice context is considered\u003C/li\u003E\n\u003Cli\u003EA validation result is attached to the request\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This enables stateless or semi-stateful validation\u003C/p\u003E\n\u003Ch3 id=\u0022user-resolution\u0022\u003E\uD83D\uDC64 User Resolution\u003C/h3\u003E\n\u003Cp\u003EThe system resolves the current user:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EBased on validated session\u003C/li\u003E\n\u003Cli\u003EUsing \u003Ccode\u003EIUserAccessor\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This produces the final user context\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022passive-request-flow\u0022\u003E\uD83D\uDD04 Passive Request Flow\u003C/h2\u003E\n\u003Cp\u003EFor normal requests:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003ERequest \n\u2192 Middleware pipeline \n\u2192 Session resolved \n\u2192 User resolved \n\u2192 Application executes \n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 No flow execution happens\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022active-flow-requests-1\u0022\u003E\uD83D\uDD10 Active Flow Requests\u003C/h2\u003E\n\u003Cp\u003EFor auth endpoints (login, refresh, etc.):\u003C/p\u003E\n\u003Cp\u003EThe lifecycle continues beyond middleware.\u003C/p\u003E\n\u003Ch3 id=\u0022step-1-flow-detection\u0022\u003EStep 1: Flow Detection\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode\u003EEndpoint \u2192 FlowType\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022step-2-context-creation\u0022\u003EStep 2: Context Creation\u003C/h3\u003E\n\u003Cp\u003EAn \u003Ccode\u003EAuthFlowContext\u003C/code\u003E is created.\u003C/p\u003E\n\u003Cp\u003EIt includes:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EClient profile\u003C/li\u003E\n\u003Cli\u003EEffective mode\u003C/li\u003E\n\u003Cli\u003ETenant\u003C/li\u003E\n\u003Cli\u003EDevice\u003C/li\u003E\n\u003Cli\u003ESession\u003C/li\u003E\n\u003Cli\u003EResponse configuration\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This defines the execution environment\u003C/p\u003E\n\u003Ch3 id=\u0022step-3-flow-execution\u0022\u003EStep 3: Flow Execution\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode\u003EAuthFlowContext \u2192 Flow Service \u2192 Orchestrator \u2192 Authority\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022step-4-state-mutation\u0022\u003EStep 4: State Mutation\u003C/h3\u003E\n\u003Cp\u003EDepending on the flow:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession may be created, updated, or revoked\u003C/li\u003E\n\u003Cli\u003ETokens may be issued\u003C/li\u003E\n\u003Cli\u003ESecurity state may change\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022step-5-response-generation\u0022\u003EStep 5: Response Generation\u003C/h3\u003E\n\u003Cp\u003EThe system writes the response:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESessionId\u003C/li\u003E\n\u003Cli\u003EAccess token\u003C/li\u003E\n\u003Cli\u003ERefresh token\u003C/li\u003E\n\u003Cli\u003ERedirect (if needed)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022example-login-request\u0022\u003E\uD83D\uDD01 Example: Login Request\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode\u003EHTTP Request \n\u2192 Tenant resolved \n\u2192 Session resolved (anonymous) \n\u2192 Flow detected (Login) \n\u2192 AuthFlowContext created \n\u2192 Credentials validated \n\u2192 Session created \n\u2192 Tokens issued \n\u2192 Response returned \n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022flow-execution-boundary\u0022\u003E\uD83D\uDD10 Flow Execution Boundary\u003C/h2\u003E\n\u003Cp\u003EAuthentication flows are only executed for endpoints explicitly marked with flow metadata.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERegular requests do not create an AuthFlowContext\u003C/li\u003E\n\u003Cli\u003EAuthFlowContext is only created during flow execution\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This ensures that authentication logic does not interfere with normal application behavior.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003E\uD83D\uDC49 Middleware prepares the request\u003Cbr /\u003E\n\uD83D\uDC49 Flows change the state\u003C/p\u003E\n" + "Html": "\n\u003Cp\u003EThis section explains what happens when a request enters UltimateAuth.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 From the moment an HTTP request arrives\u003Cbr /\u003E\n\uD83D\uDC49 Until authentication state is established or a flow is executed\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022two-types-of-requests\u0022\u003E\uD83E\uDDE0 Two Types of Requests\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth processes requests in two different ways:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022passive-requests\u0022\u003E1. Passive Requests\u003C/h3\u003E\n\u003Cp\u003ERegular application requests (page load, API call)\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022active-flow-requests\u0022\u003E2. Active Flow Requests\u003C/h3\u003E\n\u003Cp\u003EAuthentication flows (login, refresh, logout)\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Both share the same foundation, but diverge at the flow level.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022middleware-pipeline\u0022\u003E\uD83E\uDDE9 Middleware Pipeline\u003C/h2\u003E\n\u003Cp\u003EEvery request passes through the UltimateAuth middleware pipeline:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003ETenant \u2192 Session Resolution \u2192 (Validation) \u2192 User Resolution\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022tenant-resolution\u0022\u003E\uD83C\uDFE2 Tenant Resolution\u003C/h3\u003E\n\u003Cp\u003EThe system determines the tenant:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EMulti-tenant \u2192 resolved via \u003Ccode\u003EITenantResolver\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003ESingle-tenant \u2192 default context applied\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 If tenant cannot be resolved \u2192 request fails early\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022session-resolution\u0022\u003E\uD83D\uDD10 Session Resolution\u003C/h3\u003E\n\u003Cp\u003EThe system attempts to extract a session:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EFrom headers, cookies, or tokens\u003C/li\u003E\n\u003Cli\u003EConverted into a \u003Ccode\u003ESessionContext\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cpre\u003E\u003Ccode\u003ESessionId \u2192 SessionContext\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 If no session is found \u2192 request becomes anonymous\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022session-validation-resource-apis\u0022\u003E\u2714 Session Validation (Resource APIs)\u003C/h3\u003E\n\u003Cp\u003EFor API scenarios:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession is validated immediately\u003C/li\u003E\n\u003Cli\u003EDevice context is considered\u003C/li\u003E\n\u003Cli\u003EA validation result is attached to the request\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This enables stateless or semi-stateful validation\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022user-resolution\u0022\u003E\uD83D\uDC64 User Resolution\u003C/h3\u003E\n\u003Cp\u003EThe system resolves the current user:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EBased on validated session\u003C/li\u003E\n\u003Cli\u003EUsing \u003Ccode\u003EIUserAccessor\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This produces the final user context\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022passive-request-flow\u0022\u003E\uD83D\uDD04 Passive Request Flow\u003C/h2\u003E\n\u003Cp\u003EFor normal requests:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003ERequest \n\u2192 Middleware pipeline \n\u2192 Session resolved \n\u2192 User resolved \n\u2192 Application executes \n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 No flow execution happens\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022active-flow-requests-1\u0022\u003E\uD83D\uDD10 Active Flow Requests\u003C/h2\u003E\n\u003Cp\u003EFor auth endpoints (login, refresh, etc.):\u003C/p\u003E\n\u003Cp\u003EThe lifecycle continues beyond middleware.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022step-1-flow-detection\u0022\u003EStep 1: Flow Detection\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode\u003EEndpoint \u2192 FlowType\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022step-2-context-creation\u0022\u003EStep 2: Context Creation\u003C/h3\u003E\n\u003Cp\u003EAn \u003Ccode\u003EAuthFlowContext\u003C/code\u003E is created.\u003C/p\u003E\n\u003Cp\u003EIt includes:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EClient profile\u003C/li\u003E\n\u003Cli\u003EEffective mode\u003C/li\u003E\n\u003Cli\u003ETenant\u003C/li\u003E\n\u003Cli\u003EDevice\u003C/li\u003E\n\u003Cli\u003ESession\u003C/li\u003E\n\u003Cli\u003EResponse configuration\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This defines the execution environment\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022step-3-flow-execution\u0022\u003EStep 3: Flow Execution\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode\u003EAuthFlowContext \u2192 Flow Service \u2192 Orchestrator \u2192 Authority\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022step-4-state-mutation\u0022\u003EStep 4: State Mutation\u003C/h3\u003E\n\u003Cp\u003EDepending on the flow:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession may be created, updated, or revoked\u003C/li\u003E\n\u003Cli\u003ETokens may be issued\u003C/li\u003E\n\u003Cli\u003ESecurity state may change\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022step-5-response-generation\u0022\u003EStep 5: Response Generation\u003C/h3\u003E\n\u003Cp\u003EThe system writes the response:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESessionId\u003C/li\u003E\n\u003Cli\u003EAccess token\u003C/li\u003E\n\u003Cli\u003ERefresh token\u003C/li\u003E\n\u003Cli\u003ERedirect (if needed)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022example-login-request\u0022\u003E\uD83D\uDD01 Example: Login Request\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode\u003EHTTP Request \n\u2192 Tenant resolved \n\u2192 Session resolved (anonymous) \n\u2192 Flow detected (Login) \n\u2192 AuthFlowContext created \n\u2192 Credentials validated \n\u2192 Session created \n\u2192 Tokens issued \n\u2192 Response returned \n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022flow-execution-boundary\u0022\u003E\uD83D\uDD10 Flow Execution Boundary\u003C/h2\u003E\n\u003Cp\u003EAuthentication flows are only executed for endpoints explicitly marked with flow metadata.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERegular requests do not create an AuthFlowContext\u003C/li\u003E\n\u003Cli\u003EAuthFlowContext is only created during flow execution\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This ensures that authentication logic does not interfere with normal application behavior.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003E\uD83D\uDC49 Middleware prepares the request\u003Cbr /\u003E\n\uD83D\uDC49 Flows change the state\u003C/p\u003E\n", + "Headings": [ + { + "Id": "two-types-of-requests", + "Text": "\uD83E\uDDE0 Two Types of Requests", + "Level": 0 + }, + { + "Id": "passive-requests", + "Text": "1. Passive Requests", + "Level": 1 + }, + { + "Id": "active-flow-requests", + "Text": "2. Active Flow Requests", + "Level": 1 + }, + { + "Id": "middleware-pipeline", + "Text": "\uD83E\uDDE9 Middleware Pipeline", + "Level": 0 + }, + { + "Id": "tenant-resolution", + "Text": "\uD83C\uDFE2 Tenant Resolution", + "Level": 1 + }, + { + "Id": "session-resolution", + "Text": "\uD83D\uDD10 Session Resolution", + "Level": 1 + }, + { + "Id": "session-validation-resource-apis", + "Text": "\u2714 Session Validation (Resource APIs)", + "Level": 1 + }, + { + "Id": "user-resolution", + "Text": "\uD83D\uDC64 User Resolution", + "Level": 1 + }, + { + "Id": "passive-request-flow", + "Text": "\uD83D\uDD04 Passive Request Flow", + "Level": 0 + }, + { + "Id": "active-flow-requests-1", + "Text": "\uD83D\uDD10 Active Flow Requests", + "Level": 0 + }, + { + "Id": "step-1-flow-detection", + "Text": "Step 1: Flow Detection", + "Level": 1 + }, + { + "Id": "step-2-context-creation", + "Text": "Step 2: Context Creation", + "Level": 1 + }, + { + "Id": "step-3-flow-execution", + "Text": "Step 3: Flow Execution", + "Level": 1 + }, + { + "Id": "step-4-state-mutation", + "Text": "Step 4: State Mutation", + "Level": 1 + }, + { + "Id": "step-5-response-generation", + "Text": "Step 5: Response Generation", + "Level": 1 + }, + { + "Id": "example-login-request", + "Text": "\uD83D\uDD01 Example: Login Request", + "Level": 0 + }, + { + "Id": "flow-execution-boundary", + "Text": "\uD83D\uDD10 Flow Execution Boundary", + "Level": 0 + }, + { + "Id": "mental-model", + "Text": "\uD83E\uDDE0 Mental Model", + "Level": 0 + } + ] } \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/runtime-architecture.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/runtime-architecture.json index 3f442ef3..fd508bf5 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/runtime-architecture.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/runtime-architecture.json @@ -1,5 +1,72 @@ { "Slug": "fundamentals/runtime-architecture", "Title": "Runtime Architecture", - "Html": "\n\u003Cp\u003EUltimateAuth processes authentication through a structured execution pipeline.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 It is not just middleware-based authentication\u003Cbr /\u003E\n\uD83D\uDC49 It is a \u003Cstrong\u003Eflow-driven execution system\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022the-big-picture\u0022\u003E\uD83E\uDDE0 The Big Picture\u003C/h2\u003E\n\u003Cp\u003EWhen a request reaches an auth endpoint:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ERequest\n \u2192 Endpoint Filter\n \u2192 AuthFlowContext\n \u2192 Endpoint Handler\n \u2192 Flow Service\n \u2192 Orchestrator\n \u2192 Authority\n \u2192 Stores / Issuers\n \u2192 Response\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EEach step has a clearly defined responsibility.\u003C/p\u003E\n\u003Ch2 id=\u0022request-entry-point\u0022\u003E\uD83D\uDD04 Request Entry Point\u003C/h2\u003E\n\u003Cp\u003EAuthentication begins at the endpoint level.\u003C/p\u003E\n\u003Cp\u003EAn endpoint filter detects the flow:\u003C/p\u003E\n\u003Cp\u003E\u003Ccode\u003EEndpoint \u2192 FlowType (Login, Refresh, Logout\u2026)\u003C/code\u003E\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 The system knows which flow is being executed before any logic runs.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022authflowcontext-the-core-state\u0022\u003E\uD83E\uDDFE AuthFlowContext \u2014 The Core State\u003C/h2\u003E\n\u003Cp\u003EBefore any operation starts, UltimateAuth creates an AuthFlowContext.\u003C/p\u003E\n\u003Cp\u003EThis is the central object that carries:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EClient profile\u003C/li\u003E\n\u003Cli\u003EEffective authentication mode\u003C/li\u003E\n\u003Cli\u003ETenant information\u003C/li\u003E\n\u003Cli\u003EDevice context\u003C/li\u003E\n\u003Cli\u003ESession state\u003C/li\u003E\n\u003Cli\u003EResponse configuration\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This context defines the entire execution environment\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022flow-service-entry-layer\u0022\u003E\u2699\uFE0F Flow Service \u2014 Entry Layer\u003C/h2\u003E\n\u003Cp\u003EAfter the context is created, the request is passed to the Flow Service.\u003C/p\u003E\n\u003Cp\u003EThe Flow Service:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EActs as the entry point for all flows\u003C/li\u003E\n\u003Cli\u003ENormalizes execution\u003C/li\u003E\n\u003Cli\u003EDelegates work to orchestrators\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 It does not implement business logic directly\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022orchestrator-flow-coordinator\u0022\u003E\uD83E\uDDED Orchestrator \u2014 Flow Coordinator\u003C/h2\u003E\n\u003Cp\u003EThe Orchestrator manages the execution of a flow.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECoordinates multiple steps\u003C/li\u003E\n\u003Cli\u003EEnsures correct execution order\u003C/li\u003E\n\u003Cli\u003EDelegates decisions to Authority\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Think of it as the flow engine\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022authority-decision-layer\u0022\u003E\uD83D\uDD10 Authority \u2014 Decision Layer\u003C/h2\u003E\n\u003Cp\u003EThe Authority is the most critical component.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EValidates authentication state\u003C/li\u003E\n\u003Cli\u003EApplies security rules\u003C/li\u003E\n\u003Cli\u003EApproves or rejects operations\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 No sensitive operation bypasses Authority\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022services-stores\u0022\u003E\u2699\uFE0F Services \u0026amp; Stores\u003C/h2\u003E\n\u003Cp\u003EOnce decisions are made:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EServices handle domain logic\u003C/li\u003E\n\u003Cli\u003EStores handle persistence\u003C/li\u003E\n\u003Cli\u003EIssuers generate tokens or session artifacts\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 These layers do not make security decisions\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022end-to-end-example-login\u0022\u003E\uD83D\uDD01 End-to-End Example (Login)\u003C/h2\u003E\n\u003Cp\u003ELogin Request\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003E \u2192 Endpoint Filter (Login Flow)\n \u2192 AuthFlowContext created\n \u2192 Flow Service\n \u2192 Orchestrator\n \u2192 Authority validates credentials\n \u2192 Session created (Store)\n \u2192 Tokens issued (Issuer)\n \u2192 Response generated\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022why-this-architecture-matters\u0022\u003E\uD83E\uDDE0 Why This Architecture Matters\u003C/h2\u003E\n\u003Cp\u003E\u2714 Centralized Decision Making\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAuthority is always in control\u003C/li\u003E\n\u003Cli\u003ENo scattered validation logic\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\u2714 Predictable Execution\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEvery flow follows the same pipeline\u003C/li\u003E\n\u003Cli\u003ENo hidden behavior\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\u2714 Extensibility\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EReplace stores\u003C/li\u003E\n\u003Cli\u003EExtend flows\u003C/li\u003E\n\u003Cli\u003ECustomize orchestration\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\u2714 Security by Design\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ENo bypass of Authority\u003C/li\u003E\n\u003Cli\u003EContext-driven validation\u003C/li\u003E\n\u003Cli\u003EFlow-aware execution\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022relation-to-other-concepts\u0022\u003E\uD83D\uDD17 Relation to Other Concepts\u003C/h2\u003E\n\u003Cp\u003EThis architecture connects all previous concepts:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAuth Model (Root / Chain / Session) \u2192 validated in Authority\u003C/li\u003E\n\u003Cli\u003EAuth Flows \u2192 executed by Orchestrator\u003C/li\u003E\n\u003Cli\u003EAuth Modes \u2192 applied via EffectiveMode\u003C/li\u003E\n\u003Cli\u003EClient Profiles \u2192 influence behavior at runtime\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Flow defines what happens\n\uD83D\uDC49 Context defines how it happens\n\uD83D\uDC49 Authority decides if it happens\u003C/p\u003E\n\u003Ch2 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cp\u003EAuthentication is executed as a pipeline\nAuthFlowContext carries execution state\nOrchestrator coordinates flows\nAuthority enforces security\nServices and Stores execute operations\u003C/p\u003E\n\u003Ch2 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003ENow that you understand the execution model:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Continue to Request Lifecycle\u003C/p\u003E\n" + "Html": "\n\u003Cp\u003EUltimateAuth processes authentication through a structured execution pipeline.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 It is not just middleware-based authentication\u003Cbr /\u003E\n\uD83D\uDC49 It is a \u003Cstrong\u003Eflow-driven execution system\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022the-big-picture\u0022\u003E\uD83E\uDDE0 The Big Picture\u003C/h2\u003E\n\u003Cp\u003EWhen a request reaches an auth endpoint:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ERequest\n \u2192 Endpoint Filter\n \u2192 AuthFlowContext\n \u2192 Endpoint Handler\n \u2192 Flow Service\n \u2192 Orchestrator\n \u2192 Authority\n \u2192 Stores / Issuers\n \u2192 Response\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EEach step has a clearly defined responsibility.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022request-entry-point\u0022\u003E\uD83D\uDD04 Request Entry Point\u003C/h2\u003E\n\u003Cp\u003EAuthentication begins at the endpoint level.\u003C/p\u003E\n\u003Cp\u003EAn endpoint filter detects the flow:\u003C/p\u003E\n\u003Cp\u003E\u003Ccode\u003EEndpoint \u2192 FlowType (Login, Refresh, Logout\u2026)\u003C/code\u003E\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 The system knows which flow is being executed before any logic runs.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022authflowcontext-the-core-state\u0022\u003E\uD83E\uDDFE AuthFlowContext \u2014 The Core State\u003C/h2\u003E\n\u003Cp\u003EBefore any operation starts, UltimateAuth creates an AuthFlowContext.\u003C/p\u003E\n\u003Cp\u003EThis is the central object that carries:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EClient profile\u003C/li\u003E\n\u003Cli\u003EEffective authentication mode\u003C/li\u003E\n\u003Cli\u003ETenant information\u003C/li\u003E\n\u003Cli\u003EDevice context\u003C/li\u003E\n\u003Cli\u003ESession state\u003C/li\u003E\n\u003Cli\u003EResponse configuration\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This context defines the entire execution environment\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022flow-service-entry-layer\u0022\u003E\u2699\uFE0F Flow Service \u2014 Entry Layer\u003C/h2\u003E\n\u003Cp\u003EAfter the context is created, the request is passed to the Flow Service.\u003C/p\u003E\n\u003Cp\u003EThe Flow Service:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EActs as the entry point for all flows\u003C/li\u003E\n\u003Cli\u003ENormalizes execution\u003C/li\u003E\n\u003Cli\u003EDelegates work to orchestrators\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 It does not implement business logic directly\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022orchestrator-flow-coordinator\u0022\u003E\uD83E\uDDED Orchestrator \u2014 Flow Coordinator\u003C/h2\u003E\n\u003Cp\u003EThe Orchestrator manages the execution of a flow.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECoordinates multiple steps\u003C/li\u003E\n\u003Cli\u003EEnsures correct execution order\u003C/li\u003E\n\u003Cli\u003EDelegates decisions to Authority\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Think of it as the flow engine\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022authority-decision-layer\u0022\u003E\uD83D\uDD10 Authority \u2014 Decision Layer\u003C/h2\u003E\n\u003Cp\u003EThe Authority is the most critical component.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EValidates authentication state\u003C/li\u003E\n\u003Cli\u003EApplies security rules\u003C/li\u003E\n\u003Cli\u003EApproves or rejects operations\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 No sensitive operation bypasses Authority\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022services-stores\u0022\u003E\u2699\uFE0F Services \u0026amp; Stores\u003C/h2\u003E\n\u003Cp\u003EOnce decisions are made:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EServices handle domain logic\u003C/li\u003E\n\u003Cli\u003EStores handle persistence\u003C/li\u003E\n\u003Cli\u003EIssuers generate tokens or session artifacts\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 These layers do not make security decisions\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022end-to-end-example-login\u0022\u003E\uD83D\uDD01 End-to-End Example (Login)\u003C/h2\u003E\n\u003Cp\u003ELogin Request\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003E \u2192 Endpoint Filter (Login Flow)\n \u2192 AuthFlowContext created\n \u2192 Flow Service\n \u2192 Orchestrator\n \u2192 Authority validates credentials\n \u2192 Session created (Store)\n \u2192 Tokens issued (Issuer)\n \u2192 Response generated\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022why-this-architecture-matters\u0022\u003E\uD83E\uDDE0 Why This Architecture Matters\u003C/h2\u003E\n\u003Cp\u003E\u2714 Centralized Decision Making\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAuthority is always in control\u003C/li\u003E\n\u003Cli\u003ENo scattered validation logic\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\u2714 Predictable Execution\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEvery flow follows the same pipeline\u003C/li\u003E\n\u003Cli\u003ENo hidden behavior\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\u2714 Extensibility\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EReplace stores\u003C/li\u003E\n\u003Cli\u003EExtend flows\u003C/li\u003E\n\u003Cli\u003ECustomize orchestration\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\u2714 Security by Design\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ENo bypass of Authority\u003C/li\u003E\n\u003Cli\u003EContext-driven validation\u003C/li\u003E\n\u003Cli\u003EFlow-aware execution\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022relation-to-other-concepts\u0022\u003E\uD83D\uDD17 Relation to Other Concepts\u003C/h2\u003E\n\u003Cp\u003EThis architecture connects all previous concepts:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAuth Model (Root / Chain / Session) \u2192 validated in Authority\u003C/li\u003E\n\u003Cli\u003EAuth Flows \u2192 executed by Orchestrator\u003C/li\u003E\n\u003Cli\u003EAuth Modes \u2192 applied via EffectiveMode\u003C/li\u003E\n\u003Cli\u003EClient Profiles \u2192 influence behavior at runtime\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Flow defines what happens\n\uD83D\uDC49 Context defines how it happens\n\uD83D\uDC49 Authority decides if it happens\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022key-takeaways\u0022\u003E\uD83D\uDCCC Key Takeaways\u003C/h2\u003E\n\u003Cp\u003EAuthentication is executed as a pipeline\nAuthFlowContext carries execution state\nOrchestrator coordinates flows\nAuthority enforces security\nServices and Stores execute operations\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003ENow that you understand the execution model:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Continue to Request Lifecycle\u003C/p\u003E\n", + "Headings": [ + { + "Id": "the-big-picture", + "Text": "\uD83E\uDDE0 The Big Picture", + "Level": 0 + }, + { + "Id": "request-entry-point", + "Text": "\uD83D\uDD04 Request Entry Point", + "Level": 0 + }, + { + "Id": "authflowcontext-the-core-state", + "Text": "\uD83E\uDDFE AuthFlowContext \u2014 The Core State", + "Level": 0 + }, + { + "Id": "flow-service-entry-layer", + "Text": "\u2699\uFE0F Flow Service \u2014 Entry Layer", + "Level": 0 + }, + { + "Id": "orchestrator-flow-coordinator", + "Text": "\uD83E\uDDED Orchestrator \u2014 Flow Coordinator", + "Level": 0 + }, + { + "Id": "authority-decision-layer", + "Text": "\uD83D\uDD10 Authority \u2014 Decision Layer", + "Level": 0 + }, + { + "Id": "services-stores", + "Text": "\u2699\uFE0F Services \u0026 Stores", + "Level": 0 + }, + { + "Id": "end-to-end-example-login", + "Text": "\uD83D\uDD01 End-to-End Example (Login)", + "Level": 0 + }, + { + "Id": "why-this-architecture-matters", + "Text": "\uD83E\uDDE0 Why This Architecture Matters", + "Level": 0 + }, + { + "Id": "relation-to-other-concepts", + "Text": "\uD83D\uDD17 Relation to Other Concepts", + "Level": 0 + }, + { + "Id": "mental-model", + "Text": "\uD83E\uDDE0 Mental Model", + "Level": 0 + }, + { + "Id": "key-takeaways", + "Text": "\uD83D\uDCCC Key Takeaways", + "Level": 0 + }, + { + "Id": "next-step", + "Text": "\u27A1\uFE0F Next Step", + "Level": 0 + } + ] } \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/index.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/index.json index 3031305c..63bc48a1 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/index.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/index.json @@ -1,5 +1,67 @@ { "Slug": "getting-started/index", "Title": "Getting Started", - "Html": "\n\u003Cp\u003EWelcome to \u003Cstrong\u003EUltimateAuth\u003C/strong\u003E \u2014 the modern authentication framework for .NET.\u003C/p\u003E\n\u003Cp\u003EUltimateAuth is designed to make authentication \u003Cstrong\u003Esimple to use\u003C/strong\u003E, while still being \u003Cstrong\u003Epowerful, flexible, and deeply secure\u003C/strong\u003E enough for real-world applications.\u003C/p\u003E\n\u003Ch2 id=\u0022what-is-ultimateauth\u0022\u003EWhat is UltimateAuth?\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth is a \u003Cstrong\u003Eflow-driven authentication framework\u003C/strong\u003E that reimagines how authentication works in modern .NET applications.\u003C/p\u003E\n\u003Cp\u003EIt unifies:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession-based authentication\u003C/li\u003E\n\u003Cli\u003EToken-based authentication (JWT)\u003C/li\u003E\n\u003Cli\u003EPKCE flows for public clients\u003C/li\u003E\n\u003Cli\u003EMulti-client environments (Blazor Server, WASM, MAUI, APIs)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003Einto a single, consistent system.\u003C/p\u003E\n\u003Cp\u003EInstead of choosing between cookies, sessions, or tokens, UltimateAuth allows you to use \u003Cstrong\u003Ethe right model for each scenario \u2014 automatically\u003C/strong\u003E.\u003C/p\u003E\n\u003Ch2 id=\u0022what-makes-ultimateauth-different\u0022\u003EWhat Makes UltimateAuth Different?\u003C/h2\u003E\n\u003Ch3 id=\u0022session-is-a-first-class-concept\u0022\u003E\uD83D\uDD39 Session is a First-Class Concept\u003C/h3\u003E\n\u003Cp\u003EUnlike traditional systems, UltimateAuth treats sessions as a \u003Cstrong\u003Ecore domain\u003C/strong\u003E, not a side effect.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERoot \u2192 global security authority\u003C/li\u003E\n\u003Cli\u003EChain \u2192 device context\u003C/li\u003E\n\u003Cli\u003ESession \u2192 actual authentication instance\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThis allows:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EInstant revocation\u003C/li\u003E\n\u003Cli\u003EMulti-device control\u003C/li\u003E\n\u003Cli\u003ESecure session lifecycle management\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022flow-based-not-token-based\u0022\u003E\uD83D\uDD39 Flow-Based, Not Token-Based\u003C/h3\u003E\n\u003Cp\u003EUltimateAuth is not cookie-based or token-based.\u003C/p\u003E\n\u003Cp\u003EIt is \u003Cstrong\u003Eflow-based\u003C/strong\u003E:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogin is a flow\u003C/li\u003E\n\u003Cli\u003ERefresh is a flow\u003C/li\u003E\n\u003Cli\u003ERe-authentication is a flow\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Authentication becomes \u003Cstrong\u003Eexplicit, predictable, and controllable\u003C/strong\u003E\u003C/p\u003E\n\u003Ch3 id=\u0022built-for-blazor-and-modern-clients\u0022\u003E\uD83D\uDD39 Built for Blazor and Modern Clients\u003C/h3\u003E\n\u003Cp\u003EUltimateAuth is designed from the ground up for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EBlazor Server\u003C/li\u003E\n\u003Cli\u003EBlazor WASM\u003C/li\u003E\n\u003Cli\u003E.NET MAUI\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EWith:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ENative PKCE support\u003C/li\u003E\n\u003Cli\u003EBuilt-in Blazor components (\u003Ccode\u003EUAuthLoginForm\u003C/code\u003E, \u003Ccode\u003EUAuthApp\u003C/code\u003E)\u003C/li\u003E\n\u003Cli\u003EAutomatic client profile detection\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 No hacks. No manual glue code.\u003C/p\u003E\n\u003Ch3 id=\u0022runtime-aware-authentication\u0022\u003E\uD83D\uDD39 Runtime-Aware Authentication\u003C/h3\u003E\n\u003Cp\u003EAuthentication behavior is not static.\u003C/p\u003E\n\u003Cp\u003EUltimateAuth adapts based on:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EClient type\u003C/li\u003E\n\u003Cli\u003EAuth mode\u003C/li\u003E\n\u003Cli\u003ERequest context\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Same system, different optimized behavior.\u003C/p\u003E\n\u003Ch2 id=\u0022what-problems-it-solves\u0022\u003EWhat Problems It Solves\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth addresses real-world challenges:\u003C/p\u003E\n\u003Ch3 id=\u0022multiple-client-types\u0022\u003E\uD83D\uDD39 Multiple Client Types\u003C/h3\u003E\n\u003Cp\u003EBlazor Server, WASM, MAUI, and APIs all behave differently.\u003C/p\u003E\n\u003Cp\u003EUltimateAuth handles these differences automatically using \u003Cstrong\u003EClient Profiles\u003C/strong\u003E.\u003C/p\u003E\n\u003Ch3 id=\u0022session-vs-token-confusion\u0022\u003E\uD83D\uDD39 Session vs Token Confusion\u003C/h3\u003E\n\u003Cp\u003EShould you use cookies, sessions, or JWT?\u003C/p\u003E\n\u003Cp\u003EUltimateAuth removes this decision by supporting multiple auth modes and selecting the correct behavior at runtime.\u003C/p\u003E\n\u003Ch3 id=\u0022secure-session-management\u0022\u003E\uD83D\uDD39 Secure Session Management\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EDevice-aware sessions\u003C/li\u003E\n\u003Cli\u003ESession revocation\u003C/li\u003E\n\u003Cli\u003ERefresh token rotation\u003C/li\u003E\n\u003Cli\u003EReplay protection\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EAll built-in \u2014 no custom implementation required.\u003C/p\u003E\n\u003Ch3 id=\u0022complex-auth-flows\u0022\u003E\uD83D\uDD39 Complex Auth Flows\u003C/h3\u003E\n\u003Cp\u003ELogin, logout, refresh, PKCE, multi-device control etc.\u003C/p\u003E\n\u003Cp\u003EAll exposed as \u003Cstrong\u003Esimple application-level APIs\u003C/strong\u003E.\u003C/p\u003E\n\u003Ch2 id=\u0022when-to-use-ultimateauth\u0022\u003EWhen to Use UltimateAuth\u003C/h2\u003E\n\u003Cp\u003EUse UltimateAuth if:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EYou are building a modern .NET application \u003Cstrong\u003EBlazor Server, WASM or MAUI\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EYou need \u003Cstrong\u003Esession \u002B token hybrid authentication\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EYou want \u003Cstrong\u003Efull control over authentication flows\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EYou are building a \u003Cstrong\u003Emulti-client system (web \u002B mobile \u002B API)\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EYou need \u003Cstrong\u003Estrong security with extensibility\u003C/strong\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Continue to \u003Cstrong\u003EQuick Start\u003C/strong\u003E to build your first UltimateAuth application.\u003C/p\u003E\n" + "Html": "\n\u003Cp\u003EWelcome to \u003Cstrong\u003EUltimateAuth\u003C/strong\u003E \u2014 the modern authentication framework for .NET.\u003C/p\u003E\n\u003Cp\u003EUltimateAuth is designed to make authentication \u003Cstrong\u003Esimple to use\u003C/strong\u003E, while still being \u003Cstrong\u003Epowerful, flexible, and deeply secure\u003C/strong\u003E enough for real-world applications.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-is-ultimateauth\u0022\u003EWhat is UltimateAuth?\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth is a \u003Cstrong\u003Eflow-driven authentication framework\u003C/strong\u003E that reimagines how authentication works in modern .NET applications.\u003C/p\u003E\n\u003Cp\u003EIt unifies:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession-based authentication\u003C/li\u003E\n\u003Cli\u003EToken-based authentication (JWT)\u003C/li\u003E\n\u003Cli\u003EPKCE flows for public clients\u003C/li\u003E\n\u003Cli\u003EMulti-client environments (Blazor Server, WASM, MAUI, APIs)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003Einto a single, consistent system.\u003C/p\u003E\n\u003Cp\u003EInstead of choosing between cookies, sessions, or tokens, UltimateAuth allows you to use \u003Cstrong\u003Ethe right model for each scenario \u2014 automatically\u003C/strong\u003E.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-makes-ultimateauth-different\u0022\u003EWhat Makes UltimateAuth Different?\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022session-is-a-first-class-concept\u0022\u003E\uD83D\uDD39 Session is a First-Class Concept\u003C/h3\u003E\n\u003Cp\u003EUnlike traditional systems, UltimateAuth treats sessions as a \u003Cstrong\u003Ecore domain\u003C/strong\u003E, not a side effect.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERoot \u2192 global security authority\u003C/li\u003E\n\u003Cli\u003EChain \u2192 device context\u003C/li\u003E\n\u003Cli\u003ESession \u2192 actual authentication instance\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThis allows:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EInstant revocation\u003C/li\u003E\n\u003Cli\u003EMulti-device control\u003C/li\u003E\n\u003Cli\u003ESecure session lifecycle management\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022flow-based-not-token-based\u0022\u003E\uD83D\uDD39 Flow-Based, Not Token-Based\u003C/h3\u003E\n\u003Cp\u003EUltimateAuth is not cookie-based or token-based.\u003C/p\u003E\n\u003Cp\u003EIt is \u003Cstrong\u003Eflow-based\u003C/strong\u003E:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogin is a flow\u003C/li\u003E\n\u003Cli\u003ERefresh is a flow\u003C/li\u003E\n\u003Cli\u003ERe-authentication is a flow\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Authentication becomes \u003Cstrong\u003Eexplicit, predictable, and controllable\u003C/strong\u003E\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022built-for-blazor-and-modern-clients\u0022\u003E\uD83D\uDD39 Built for Blazor and Modern Clients\u003C/h3\u003E\n\u003Cp\u003EUltimateAuth is designed from the ground up for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EBlazor Server\u003C/li\u003E\n\u003Cli\u003EBlazor WASM\u003C/li\u003E\n\u003Cli\u003E.NET MAUI\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EWith:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ENative PKCE support\u003C/li\u003E\n\u003Cli\u003EBuilt-in Blazor components (\u003Ccode\u003EUAuthLoginForm\u003C/code\u003E, \u003Ccode\u003EUAuthApp\u003C/code\u003E)\u003C/li\u003E\n\u003Cli\u003EAutomatic client profile detection\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 No hacks. No manual glue code.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022runtime-aware-authentication\u0022\u003E\uD83D\uDD39 Runtime-Aware Authentication\u003C/h3\u003E\n\u003Cp\u003EAuthentication behavior is not static.\u003C/p\u003E\n\u003Cp\u003EUltimateAuth adapts based on:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EClient type\u003C/li\u003E\n\u003Cli\u003EAuth mode\u003C/li\u003E\n\u003Cli\u003ERequest context\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Same system, different optimized behavior.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-problems-it-solves\u0022\u003EWhat Problems It Solves\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth addresses real-world challenges:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022multiple-client-types\u0022\u003E\uD83D\uDD39 Multiple Client Types\u003C/h3\u003E\n\u003Cp\u003EBlazor Server, WASM, MAUI, and APIs all behave differently.\u003C/p\u003E\n\u003Cp\u003EUltimateAuth handles these differences automatically using \u003Cstrong\u003EClient Profiles\u003C/strong\u003E.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022session-vs-token-confusion\u0022\u003E\uD83D\uDD39 Session vs Token Confusion\u003C/h3\u003E\n\u003Cp\u003EShould you use cookies, sessions, or JWT?\u003C/p\u003E\n\u003Cp\u003EUltimateAuth removes this decision by supporting multiple auth modes and selecting the correct behavior at runtime.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022secure-session-management\u0022\u003E\uD83D\uDD39 Secure Session Management\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EDevice-aware sessions\u003C/li\u003E\n\u003Cli\u003ESession revocation\u003C/li\u003E\n\u003Cli\u003ERefresh token rotation\u003C/li\u003E\n\u003Cli\u003EReplay protection\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EAll built-in \u2014 no custom implementation required.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022complex-auth-flows\u0022\u003E\uD83D\uDD39 Complex Auth Flows\u003C/h3\u003E\n\u003Cp\u003ELogin, logout, refresh, PKCE, multi-device control etc.\u003C/p\u003E\n\u003Cp\u003EAll exposed as \u003Cstrong\u003Esimple application-level APIs\u003C/strong\u003E.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022when-to-use-ultimateauth\u0022\u003EWhen to Use UltimateAuth\u003C/h2\u003E\n\u003Cp\u003EUse UltimateAuth if:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EYou are building a modern .NET application \u003Cstrong\u003EBlazor Server, WASM or MAUI\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EYou need \u003Cstrong\u003Esession \u002B token hybrid authentication\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EYou want \u003Cstrong\u003Efull control over authentication flows\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EYou are building a \u003Cstrong\u003Emulti-client system (web \u002B mobile \u002B API)\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EYou need \u003Cstrong\u003Estrong security with extensibility\u003C/strong\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Continue to \u003Cstrong\u003EQuick Start\u003C/strong\u003E to build your first UltimateAuth application.\u003C/p\u003E\n", + "Headings": [ + { + "Id": "what-is-ultimateauth", + "Text": "What is UltimateAuth?", + "Level": 0 + }, + { + "Id": "what-makes-ultimateauth-different", + "Text": "What Makes UltimateAuth Different?", + "Level": 0 + }, + { + "Id": "session-is-a-first-class-concept", + "Text": "\uD83D\uDD39 Session is a First-Class Concept", + "Level": 1 + }, + { + "Id": "flow-based-not-token-based", + "Text": "\uD83D\uDD39 Flow-Based, Not Token-Based", + "Level": 1 + }, + { + "Id": "built-for-blazor-and-modern-clients", + "Text": "\uD83D\uDD39 Built for Blazor and Modern Clients", + "Level": 1 + }, + { + "Id": "runtime-aware-authentication", + "Text": "\uD83D\uDD39 Runtime-Aware Authentication", + "Level": 1 + }, + { + "Id": "what-problems-it-solves", + "Text": "What Problems It Solves", + "Level": 0 + }, + { + "Id": "multiple-client-types", + "Text": "\uD83D\uDD39 Multiple Client Types", + "Level": 1 + }, + { + "Id": "session-vs-token-confusion", + "Text": "\uD83D\uDD39 Session vs Token Confusion", + "Level": 1 + }, + { + "Id": "secure-session-management", + "Text": "\uD83D\uDD39 Secure Session Management", + "Level": 1 + }, + { + "Id": "complex-auth-flows", + "Text": "\uD83D\uDD39 Complex Auth Flows", + "Level": 1 + }, + { + "Id": "when-to-use-ultimateauth", + "Text": "When to Use UltimateAuth", + "Level": 0 + } + ] } \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/quickstart.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/quickstart.json index b74fa2b9..8caf8257 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/quickstart.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/quickstart.json @@ -1,5 +1,62 @@ { "Slug": "getting-started/quickstart", "Title": "QuickStart", - "Html": "\n\u003Cp\u003EIn this guide, you will set up UltimateAuth in a few minutes and perform your \u003Cstrong\u003Efirst login\u003C/strong\u003E.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch2 id=\u0022create-a-project\u0022\u003E1. Create a Project\u003C/h2\u003E\n\u003Cp\u003ECreate a new Blazor Server web app:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-bash\u0022\u003Edotnet new blazorserver -n UltimateAuthDemo\ncd UltimateAuthDemo\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022install-packages\u0022\u003E2. Install Packages\u003C/h2\u003E\n\u003Cp\u003EAdd UltimateAuth packages:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Edotnet add package CodeBeam.UltimateAuth.Server\ndotnet add package CodeBeam.UltimateAuth.Client.Blazor\ndotnet add package CodeBeam.UltimateAuth.InMemory.Bundle\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022configure-services\u0022\u003E3. Configure Services\u003C/h2\u003E\n\u003Cp\u003EUpdate your Program.cs:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services\n .AddUltimateAuthServer()\n .AddUltimateAuthInMemory();\n\nbuilder.Services\n .AddUltimateAuthClientBlazor();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022configure-middleware\u0022\u003E4. Configure Middleware\u003C/h2\u003E\n\u003Cp\u003EIn \u003Ccode\u003EProgram.cs\u003C/code\u003E\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eapp.UseUltimateAuthWithAspNetCore();\napp.MapUltimateAuthEndpoints();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022enable-blazor-integration\u0022\u003E5. Enable Blazor Integration\u003C/h2\u003E\n\u003Cp\u003EIn \u003Ccode\u003EProgram.cs\u003C/code\u003E\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eapp.MapRazorComponents\u0026lt;App\u0026gt;()\n .AddInteractiveServerRenderMode() // or webassembly (depends on your application type)\n .AddUltimateAuthRoutes(UAuthAssemblies.BlazorClient());\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022add-uauth-script\u0022\u003E6. Add UAuth Script\u003C/h2\u003E\n\u003Cp\u003EAdd this to \u003Ccode\u003EApp.razor\u003C/code\u003E or \u003Ccode\u003Eindex.html\u003C/code\u003E:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003E\u0026lt;script src=\u0026quot;_content/CodeBeam.UltimateAuth.Client.Blazor/uauth.min.js\u0026quot;\u0026gt;\u0026lt;/script\u0026gt;\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022configure-application-lifecycle\u0022\u003E7. Configure Application Lifecycle\u003C/h2\u003E\n\u003Cp\u003EReplace \u003Ccode\u003ERoutes.razor\u003C/code\u003E with this code:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003E\u0026lt;UAuthApp UseBuiltInRouter=\u0026quot;true\u0026quot; AppAssembly=\u0026quot;typeof(Program).Assembly\u0026quot; DefaultLayout=\u0026quot;typeof(Layout.MainLayout)\u0026quot; /\u0026gt;\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022perform-your-first-login\u0022\u003E8. Perform Your First Login\u003C/h2\u003E\n\u003Cp\u003EExample using IUAuthClient:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003E[Inject] IUAuthClient UAuthClient { get; set; } = null!;\n\nprivate async Task Login()\n{\n await UAuthClient.Flows.LoginAsync(new LoginRequest\n {\n Identifier = \u0026quot;demo\u0026quot;,\n Secret = \u0026quot;password\u0026quot;\n });\n}\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022thats-it\u0022\u003E\uD83C\uDF89 That\u2019s It\u003C/h2\u003E\n\u003Cp\u003EYou now have a working authentication system with:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession-based authentication\u003C/li\u003E\n\u003Cli\u003EAutomatic client detection\u003C/li\u003E\n\u003Cli\u003EBuilt-in login flow\u003C/li\u003E\n\u003Cli\u003ESecure session handling\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022what-just-happened\u0022\u003EWhat Just Happened?\u003C/h2\u003E\n\u003Cp\u003EWhen you logged in:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EA session (with root and chain) was created on the server,\u003C/li\u003E\n\u003Cli\u003EYour client received an authentication grant (cookie or token),\u003C/li\u003E\n\u003Cli\u003EUltimateAuth established your auth state automatically.\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You didn\u2019t manage cookies, tokens, or redirects manually.\u003C/p\u003E\n\u003Ch2 id=\u0022next-steps\u0022\u003ENext Steps\u003C/h2\u003E\n\u003Cp\u003EDiscover the setup for real world applications with entity framework core.\u003C/p\u003E\n" + "Html": "\n\u003Cp\u003EIn this guide, you will set up UltimateAuth in a few minutes and perform your \u003Cstrong\u003Efirst login\u003C/strong\u003E.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022create-a-project\u0022\u003E1. Create a Project\u003C/h2\u003E\n\u003Cp\u003ECreate a new Blazor Server web app:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-bash\u0022\u003Edotnet new blazorserver -n UltimateAuthDemo\ncd UltimateAuthDemo\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022install-packages\u0022\u003E2. Install Packages\u003C/h2\u003E\n\u003Cp\u003EAdd UltimateAuth packages:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Edotnet add package CodeBeam.UltimateAuth.Server\ndotnet add package CodeBeam.UltimateAuth.Client.Blazor\ndotnet add package CodeBeam.UltimateAuth.InMemory.Bundle\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022configure-services\u0022\u003E3. Configure Services\u003C/h2\u003E\n\u003Cp\u003EUpdate your Program.cs:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services\n .AddUltimateAuthServer()\n .AddUltimateAuthInMemory();\n\nbuilder.Services\n .AddUltimateAuthClientBlazor();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022configure-middleware\u0022\u003E4. Configure Middleware\u003C/h2\u003E\n\u003Cp\u003EIn \u003Ccode\u003EProgram.cs\u003C/code\u003E\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eapp.UseUltimateAuthWithAspNetCore();\napp.MapUltimateAuthEndpoints();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022enable-blazor-integration\u0022\u003E5. Enable Blazor Integration\u003C/h2\u003E\n\u003Cp\u003EIn \u003Ccode\u003EProgram.cs\u003C/code\u003E\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eapp.MapRazorComponents\u0026lt;App\u0026gt;()\n .AddInteractiveServerRenderMode() // or webassembly (depends on your application type)\n .AddUltimateAuthRoutes(UAuthAssemblies.BlazorClient());\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022add-uauth-script\u0022\u003E6. Add UAuth Script\u003C/h2\u003E\n\u003Cp\u003EAdd this to \u003Ccode\u003EApp.razor\u003C/code\u003E or \u003Ccode\u003Eindex.html\u003C/code\u003E:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003E\u0026lt;script src=\u0026quot;_content/CodeBeam.UltimateAuth.Client.Blazor/uauth.min.js\u0026quot;\u0026gt;\u0026lt;/script\u0026gt;\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022configure-application-lifecycle\u0022\u003E7. Configure Application Lifecycle\u003C/h2\u003E\n\u003Cp\u003EReplace \u003Ccode\u003ERoutes.razor\u003C/code\u003E with this code:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003E\u0026lt;UAuthApp UseBuiltInRouter=\u0026quot;true\u0026quot; AppAssembly=\u0026quot;typeof(Program).Assembly\u0026quot; DefaultLayout=\u0026quot;typeof(Layout.MainLayout)\u0026quot; /\u0026gt;\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022perform-your-first-login\u0022\u003E8. Perform Your First Login\u003C/h2\u003E\n\u003Cp\u003EExample using IUAuthClient:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003E[Inject] IUAuthClient UAuthClient { get; set; } = null!;\n\nprivate async Task Login()\n{\n await UAuthClient.Flows.LoginAsync(new LoginRequest\n {\n Identifier = \u0026quot;demo\u0026quot;,\n Secret = \u0026quot;password\u0026quot;\n });\n}\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022thats-it\u0022\u003E\uD83C\uDF89 That\u2019s It\u003C/h2\u003E\n\u003Cp\u003EYou now have a working authentication system with:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession-based authentication\u003C/li\u003E\n\u003Cli\u003EAutomatic client detection\u003C/li\u003E\n\u003Cli\u003EBuilt-in login flow\u003C/li\u003E\n\u003Cli\u003ESecure session handling\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-just-happened\u0022\u003EWhat Just Happened?\u003C/h2\u003E\n\u003Cp\u003EWhen you logged in:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EA session (with root and chain) was created on the server,\u003C/li\u003E\n\u003Cli\u003EYour client received an authentication grant (cookie or token),\u003C/li\u003E\n\u003Cli\u003EUltimateAuth established your auth state automatically.\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You didn\u2019t manage cookies, tokens, or redirects manually.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-steps\u0022\u003ENext Steps\u003C/h2\u003E\n\u003Cp\u003EDiscover the setup for real world applications with entity framework core.\u003C/p\u003E\n", + "Headings": [ + { + "Id": "create-a-project", + "Text": "1. Create a Project", + "Level": 0 + }, + { + "Id": "install-packages", + "Text": "2. Install Packages", + "Level": 0 + }, + { + "Id": "configure-services", + "Text": "3. Configure Services", + "Level": 0 + }, + { + "Id": "configure-middleware", + "Text": "4. Configure Middleware", + "Level": 0 + }, + { + "Id": "enable-blazor-integration", + "Text": "5. Enable Blazor Integration", + "Level": 0 + }, + { + "Id": "add-uauth-script", + "Text": "6. Add UAuth Script", + "Level": 0 + }, + { + "Id": "configure-application-lifecycle", + "Text": "7. Configure Application Lifecycle", + "Level": 0 + }, + { + "Id": "perform-your-first-login", + "Text": "8. Perform Your First Login", + "Level": 0 + }, + { + "Id": "thats-it", + "Text": "\uD83C\uDF89 That\u2019s It", + "Level": 0 + }, + { + "Id": "what-just-happened", + "Text": "What Just Happened?", + "Level": 0 + }, + { + "Id": "next-steps", + "Text": "Next Steps", + "Level": 0 + } + ] } \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/real-world-setup.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/real-world-setup.json index 7ff27001..24236da8 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/real-world-setup.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/getting-started/real-world-setup.json @@ -1,5 +1,47 @@ { "Slug": "getting-started/real-world-setup", "Title": "Real World Setup", - "Html": "\n\u003Cp\u003EThe Quick Start uses an in-memory setup for simplicity.\nIn real-world applications, you should replace it with a persistent configuration as shown below.\u003C/p\u003E\n\u003Cp\u003EIn real applications, you will typically configure:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EA persistent database\u003C/li\u003E\n\u003Cli\u003EAn appropriate client profile\u003C/li\u003E\n\u003Cli\u003EA suitable authentication mode\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThis guide shows how to set up UltimateAuth for real-world scenarios.\u003C/p\u003E\n\u003Ch2 id=\u0022using-entity-framework-core\u0022\u003E\uD83D\uDDC4\uFE0F Using Entity Framework Core\u003C/h2\u003E\n\u003Cp\u003EFor production, you should use a persistent store. In this setup, you no longer need the \u003Ccode\u003ECodeBeam.UltimateAuth.InMemory.Bundle\u003C/code\u003E package.\u003C/p\u003E\n\u003Ch3 id=\u0022install-packages\u0022\u003EInstall Packages\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-bash\u0022\u003Edotnet add package CodeBeam.UltimateAuth.EntityFrameworkCore.Bundle\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022configure-services\u0022\u003EConfigure Services\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services\n .AddUltimateAuthServer()\n .AddUltimateAuthEntityFrameworkCore(db =\u0026gt;\n {\n db.UseSqlite(\u0026quot;Data Source=uauth.db\u0026quot;);\n // or UseSqlServer / UseNpgsql\n });\n\nbuilder.Services\n .AddUltimateAuthClientBlazor();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022create-database-migrations\u0022\u003ECreate Database \u0026amp; Migrations\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-bash\u0022\u003Edotnet ef migrations add InitUAuth\ndotnet ef database update\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003Eor\u003C/p\u003E\n\u003Cp\u003EIf you are using Visual Studio, you can run these commands in Package Manager Console*:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-bash\u0022\u003EAdd-Migration InitUAuth -Context UAuthDbContext\nUpdate-Database -Context UAuthDbContext\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E*Needs \u003Ccode\u003EMicrosoft.EntityFrameworkCore.Design\u003C/code\u003E and \u003Ccode\u003EMicrosoft.EntityFrameworkCore.Tools\u003C/code\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022configure-services-with-options\u0022\u003EConfigure Services With Options\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth provides rich options for server and client service registration.\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthServer(o =\u0026gt; {\n o.Diagnostics.EnableRefreshDetails = true;\n o.Login.MaxFailedAttempts = 4;\n o.Identifiers.AllowMultipleUsernames = true;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022blazor-wasm-setup\u0022\u003EBlazor WASM Setup\u003C/h2\u003E\n\u003Cp\u003EBlazor WASM applications run entirely on the client and cannot securely handle credentials.\nFor this reason, UltimateAuth uses a dedicated Auth server called \u003Cstrong\u003EUAuthHub\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EWASM \u003Ccode\u003EProgram.cs\u003C/code\u003E:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthClientBlazor(o =\u0026gt;\n{\n o.Endpoints.BasePath = \u0026quot;https://localhost:6110/auth\u0026quot;; // UAuthHub URL\n o.Pkce.ReturnUrl = \u0026quot;https://localhost:6130/home\u0026quot;; // Your (WASM) application domain \u002B return path\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EUAuthHub \u003Ccode\u003EProgram.cs\u003C/code\u003E:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthServer()\n .AddUltimateAuthInMemory()\n .AddUAuthHub(o =\u0026gt; o.AllowedClientOrigins.Add(\u0026quot;https://localhost:6130\u0026quot;)); // WASM application\u0027s URL\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EUAuthHub Pipeline Configuration\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eapp.MapUltimateAuthEndpoints();\napp.MapUAuthHub();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003E\u2139\uFE0F UltimateAuth automatically selects the appropriate authentication mode (PureOpaque, Hybrid, etc.) based on the client type.\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Ch2 id=\u0022resourceapi-setup\u0022\u003EResourceApi Setup\u003C/h2\u003E\n\u003Cp\u003EYou may want to secure your custom API with UltimateAuth. UltimateAuth provides a lightweight option for this case. (ResourceApi doesn\u0027t have to be a blazor application, it can be any server-side project like MVC.)\u003C/p\u003E\n\u003Cp\u003EResourceApi\u0027s \u003Ccode\u003EProgram.cs\u003C/code\u003E\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthResourceApi(o =\u0026gt;\n {\n o.UAuthHubBaseUrl = \u0026quot;https://localhost:6110\u0026quot;;\n o.AllowedClientOrigins.Add(\u0026quot;https://localhost:6130\u0026quot;);\n });\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EConfigure pipeline:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eapp.UseUltimateAuthResourceApiWithAspNetCore();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003ENotes:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EResourceApi should connect with an UAuthHub, not a pure-server. Make sure \u003Ccode\u003E.AddUAuthHub()\u003C/code\u003E after calling \u003Ccode\u003Ebuilder.Services.AddUltimateAuthServer()\u003C/code\u003E.\u003C/li\u003E\n\u003Cli\u003EUltimateAuth automatically configures CORS based on the provided origins.\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EUse ResourceApi when:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EYou have a separate backend API\u003C/li\u003E\n\u003Cli\u003EYou want to validate sessions or tokens externally\u003C/li\u003E\n\u003Cli\u003EYour API is not hosting UltimateAuth directly\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022how-to-think-about-setup\u0022\u003E\uD83E\uDDE0 How to Think About Setup\u003C/h2\u003E\n\u003Cp\u003EIn UltimateAuth:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EThe \u003Cstrong\u003EServer\u003C/strong\u003E manages authentication flows and sessions\u003C/li\u003E\n\u003Cli\u003EThe \u003Cstrong\u003EClient\u003C/strong\u003E interacts through flows (not tokens directly)\u003C/li\u003E\n\u003Cli\u003EThe \u003Cstrong\u003EStorage layer\u003C/strong\u003E (InMemory / EF Core) defines persistence\u003C/li\u003E\n\u003Cli\u003EThe \u003Cstrong\u003EApplication type\u003C/strong\u003E determines runtime behavior\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You configure the system once, and UltimateAuth adapts automatically.\u003C/p\u003E\n" + "Html": "\n\u003Cp\u003EThe Quick Start uses an in-memory setup for simplicity.\nIn real-world applications, you should replace it with a persistent configuration as shown below.\u003C/p\u003E\n\u003Cp\u003EIn real applications, you will typically configure:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EA persistent database\u003C/li\u003E\n\u003Cli\u003EAn appropriate client profile\u003C/li\u003E\n\u003Cli\u003EA suitable authentication mode\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThis guide shows how to set up UltimateAuth for real-world scenarios.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022using-entity-framework-core\u0022\u003E\uD83D\uDDC4\uFE0F Using Entity Framework Core\u003C/h2\u003E\n\u003Cp\u003EFor production, you should use a persistent store. In this setup, you no longer need the \u003Ccode\u003ECodeBeam.UltimateAuth.InMemory.Bundle\u003C/code\u003E package.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022install-packages\u0022\u003EInstall Packages\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-bash\u0022\u003Edotnet add package CodeBeam.UltimateAuth.EntityFrameworkCore.Bundle\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022configure-services\u0022\u003EConfigure Services\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services\n .AddUltimateAuthServer()\n .AddUltimateAuthEntityFrameworkCore(db =\u0026gt;\n {\n db.UseSqlite(\u0026quot;Data Source=uauth.db\u0026quot;);\n // or UseSqlServer / UseNpgsql\n });\n\nbuilder.Services\n .AddUltimateAuthClientBlazor();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022create-database-migrations\u0022\u003ECreate Database \u0026amp; Migrations\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-bash\u0022\u003Edotnet ef migrations add InitUAuth\ndotnet ef database update\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003Eor\u003C/p\u003E\n\u003Cp\u003EIf you are using Visual Studio, you can run these commands in Package Manager Console*:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-bash\u0022\u003EAdd-Migration InitUAuth -Context UAuthDbContext\nUpdate-Database -Context UAuthDbContext\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E*Needs \u003Ccode\u003EMicrosoft.EntityFrameworkCore.Design\u003C/code\u003E and \u003Ccode\u003EMicrosoft.EntityFrameworkCore.Tools\u003C/code\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022configure-services-with-options\u0022\u003EConfigure Services With Options\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth provides rich options for server and client service registration.\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthServer(o =\u0026gt; {\n o.Diagnostics.EnableRefreshDetails = true;\n o.Login.MaxFailedAttempts = 4;\n o.Identifiers.AllowMultipleUsernames = true;\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022blazor-wasm-setup\u0022\u003EBlazor WASM Setup\u003C/h2\u003E\n\u003Cp\u003EBlazor WASM applications run entirely on the client and cannot securely handle credentials.\nFor this reason, UltimateAuth uses a dedicated Auth server called \u003Cstrong\u003EUAuthHub\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EWASM \u003Ccode\u003EProgram.cs\u003C/code\u003E:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthClientBlazor(o =\u0026gt;\n{\n o.Endpoints.BasePath = \u0026quot;https://localhost:6110/auth\u0026quot;; // UAuthHub URL\n o.Pkce.ReturnUrl = \u0026quot;https://localhost:6130/home\u0026quot;; // Your (WASM) application domain \u002B return path\n});\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EUAuthHub \u003Ccode\u003EProgram.cs\u003C/code\u003E:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthServer()\n .AddUltimateAuthInMemory()\n .AddUAuthHub(o =\u0026gt; o.AllowedClientOrigins.Add(\u0026quot;https://localhost:6130\u0026quot;)); // WASM application\u0027s URL\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EUAuthHub Pipeline Configuration\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eapp.MapUltimateAuthEndpoints();\napp.MapUAuthHub();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cblockquote\u003E\n\u003Cp\u003E\u2139\uFE0F UltimateAuth automatically selects the appropriate authentication mode (PureOpaque, Hybrid, etc.) based on the client type.\u003C/p\u003E\n\u003C/blockquote\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022resourceapi-setup\u0022\u003EResourceApi Setup\u003C/h2\u003E\n\u003Cp\u003EYou may want to secure your custom API with UltimateAuth. UltimateAuth provides a lightweight option for this case. (ResourceApi doesn\u0027t have to be a blazor application, it can be any server-side project like MVC.)\u003C/p\u003E\n\u003Cp\u003EResourceApi\u0027s \u003Ccode\u003EProgram.cs\u003C/code\u003E\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Ebuilder.Services.AddUltimateAuthResourceApi(o =\u0026gt;\n {\n o.UAuthHubBaseUrl = \u0026quot;https://localhost:6110\u0026quot;;\n o.AllowedClientOrigins.Add(\u0026quot;https://localhost:6130\u0026quot;);\n });\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EConfigure pipeline:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-csharp\u0022\u003Eapp.UseUltimateAuthResourceApiWithAspNetCore();\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003ENotes:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EResourceApi should connect with an UAuthHub, not a pure-server. Make sure \u003Ccode\u003E.AddUAuthHub()\u003C/code\u003E after calling \u003Ccode\u003Ebuilder.Services.AddUltimateAuthServer()\u003C/code\u003E.\u003C/li\u003E\n\u003Cli\u003EUltimateAuth automatically configures CORS based on the provided origins.\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EUse ResourceApi when:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EYou have a separate backend API\u003C/li\u003E\n\u003Cli\u003EYou want to validate sessions or tokens externally\u003C/li\u003E\n\u003Cli\u003EYour API is not hosting UltimateAuth directly\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022how-to-think-about-setup\u0022\u003E\uD83E\uDDE0 How to Think About Setup\u003C/h2\u003E\n\u003Cp\u003EIn UltimateAuth:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EThe \u003Cstrong\u003EServer\u003C/strong\u003E manages authentication flows and sessions\u003C/li\u003E\n\u003Cli\u003EThe \u003Cstrong\u003EClient\u003C/strong\u003E interacts through flows (not tokens directly)\u003C/li\u003E\n\u003Cli\u003EThe \u003Cstrong\u003EStorage layer\u003C/strong\u003E (InMemory / EF Core) defines persistence\u003C/li\u003E\n\u003Cli\u003EThe \u003Cstrong\u003EApplication type\u003C/strong\u003E determines runtime behavior\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You configure the system once, and UltimateAuth adapts automatically.\u003C/p\u003E\n", + "Headings": [ + { + "Id": "using-entity-framework-core", + "Text": "\uD83D\uDDC4\uFE0F Using Entity Framework Core", + "Level": 0 + }, + { + "Id": "install-packages", + "Text": "Install Packages", + "Level": 1 + }, + { + "Id": "configure-services", + "Text": "Configure Services", + "Level": 1 + }, + { + "Id": "create-database-migrations", + "Text": "Create Database \u0026 Migrations", + "Level": 1 + }, + { + "Id": "configure-services-with-options", + "Text": "Configure Services With Options", + "Level": 0 + }, + { + "Id": "blazor-wasm-setup", + "Text": "Blazor WASM Setup", + "Level": 0 + }, + { + "Id": "resourceapi-setup", + "Text": "ResourceApi Setup", + "Level": 0 + }, + { + "Id": "how-to-think-about-setup", + "Text": "\uD83E\uDDE0 How to Think About Setup", + "Level": 0 + } + ] } \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/authorization-domain.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/authorization-domain.json index 2c8562a0..d69338a7 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/authorization-domain.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/authorization-domain.json @@ -1,5 +1,77 @@ { "Slug": "plugin-domains/authorization-domain", "Title": "Authorization", - "Html": "\n\u003Cp\u003EUltimateAuth provides a flexible and extensible authorization system based on:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERoles\u003C/li\u003E\n\u003Cli\u003EPermissions\u003C/li\u003E\n\u003Cli\u003EPolicies\u003C/li\u003E\n\u003Cli\u003EAccess orchestration\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022core-concepts\u0022\u003E\uD83E\uDDE9 Core Concepts\u003C/h2\u003E\n\u003Ch3 id=\u0022permissions\u0022\u003E\uD83D\uDD11 Permissions\u003C/h3\u003E\n\u003Cp\u003EIn UltimateAuth, permissions are not just arbitrary strings.\u003C/p\u003E\n\u003Cp\u003EThey follow a \u003Cstrong\u003Estructured action model\u003C/strong\u003E.\u003C/p\u003E\n\u003Ch4 id=\u0022permission-structure\u0022\u003E\uD83E\uDDE9 Permission Structure\u003C/h4\u003E\n\u003Cp\u003EPermissions are built using a consistent format:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003Eresource.operation.scope\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003Eor\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003Eresource.subresource.operation.scope\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch4 id=\u0022examples\u0022\u003E\u2705 Examples\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003Eusers.create.admin\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003Eusers.profile.update.self\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003Esessions.revokechain.admin\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003Ecredentials.change.self\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This structure is not accidental \u2014\u003Cbr /\u003E\nit is \u003Cstrong\u003Edesigned for consistency, readability, and policy evaluation\u003C/strong\u003E.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch3 id=\u0022built-in-action-catalog\u0022\u003E\u2699\uFE0F Built-in Action Catalog\u003C/h3\u003E\n\u003Cp\u003EUltimateAuth provides a predefined action catalog.\u003C/p\u003E\n\u003Cp\u003EExamples:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003Eflows.logout.self\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003Esessions.listchains.admin\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003Eusers.delete.self\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003Ecredentials.revoke.admin\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003Eauthorization.roles.assign.admin\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This ensures:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ENo magic strings\u003C/li\u003E\n\u003Cli\u003EDiscoverable permissions\u003C/li\u003E\n\u003Cli\u003EConsistent naming across the system\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch3 id=\u0022scope-semantics\u0022\u003E\uD83E\uDDE0 Scope Semantics\u003C/h3\u003E\n\u003Cp\u003EThe last part of the permission defines \u003Cstrong\u003Escope\u003C/strong\u003E:\u003C/p\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EScope\u003C/th\u003E\n\u003Cth\u003EMeaning\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Ccode\u003Eself\u003C/code\u003E\u003C/td\u003E\n\u003Ctd\u003EUser acts on own resources\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Ccode\u003Eadmin\u003C/code\u003E\u003C/td\u003E\n\u003Ctd\u003EUser acts on other users\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Ccode\u003Eanonymous\u003C/code\u003E\u003C/td\u003E\n\u003Ctd\u003ENo authentication required\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Cbr\u003E\n\u003Ch3 id=\u0022wildcards-grouping\u0022\u003E\uD83C\uDF32 Wildcards \u0026amp; Grouping\u003C/h3\u003E\n\u003Cp\u003EPermissions support hierarchical matching:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003Eusers.*\u003C/code\u003E \u2192 all user actions\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003Eusers.profile.*\u003C/code\u003E \u2192 all profile operations\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003E*\u003C/code\u003E \u2192 full access\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022normalization\u0022\u003E\u26A1 Normalization\u003C/h3\u003E\n\u003Cp\u003EPermissions are automatically normalized:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EFull coverage \u2192 replaced with \u003Ccode\u003E*\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003EFull group \u2192 replaced with \u003Ccode\u003Eprefix.*\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch3 id=\u0022role\u0022\u003ERole\u003C/h3\u003E\n\u003Cp\u003EA role is a collection of permissions.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERoles are tenant-scoped\u003C/li\u003E\n\u003Cli\u003ERoles can be dynamically updated\u003C/li\u003E\n\u003Cli\u003EPermissions are normalized internally\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022userrole\u0022\u003EUserRole\u003C/h3\u003E\n\u003Cp\u003EUsers are assigned roles:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EMany-to-many relationship\u003C/li\u003E\n\u003Cli\u003EAssignment is timestamped\u003C/li\u003E\n\u003Cli\u003ERole resolution is runtime-based\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022permission-resolution\u0022\u003E\uD83D\uDD04 Permission Resolution\u003C/h2\u003E\n\u003Cp\u003EPermissions are evaluated using:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EExact match\u003C/li\u003E\n\u003Cli\u003EPrefix match\u003C/li\u003E\n\u003Cli\u003EWildcard match\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003ECompiledPermissionSet optimizes runtime checks.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022claims-integration\u0022\u003E\uD83E\uDDE0 Claims Integration\u003C/h2\u003E\n\u003Cp\u003EAuthorization integrates with authentication via claims:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERoles \u2192 \u003Ccode\u003EClaimTypes.Role\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003EPermissions \u2192 \u003Ccode\u003Epermission\u003C/code\u003E claim\u003C/li\u003E\n\u003Cli\u003ETenant \u2192 \u003Ccode\u003Etenant\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThis allows:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EToken-based authorization\u003C/li\u003E\n\u003Cli\u003EStateless permission checks (for JWT modes)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022authorization-flow\u0022\u003E\u2699\uFE0F Authorization Flow\u003C/h2\u003E\n\u003Cp\u003EAuthorization is executed through:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 AccessOrchestrator\u003C/p\u003E\n\u003Cp\u003ESteps:\u003C/p\u003E\n\u003Col\u003E\n\u003Cli\u003EBuild AccessContext\u003C/li\u003E\n\u003Cli\u003EExecute policies\u003C/li\u003E\n\u003Cli\u003EAllow or deny operation\u003C/li\u003E\n\u003C/ol\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022policies\u0022\u003E\uD83D\uDEE1 Policies\u003C/h2\u003E\n\u003Cp\u003EPolicies are the core of authorization logic.\u003C/p\u003E\n\u003Cp\u003EDefault policies include:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERequireAuthenticated\u003C/li\u003E\n\u003Cli\u003EDenyCrossTenant\u003C/li\u003E\n\u003Cli\u003ERequireActiveUser\u003C/li\u003E\n\u003Cli\u003ERequireSelf\u003C/li\u003E\n\u003Cli\u003ERequireSystem\u003C/li\u003E\n\u003Cli\u003EMustHavePermission\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022plugin-integration\u0022\u003E\uD83D\uDD0C Plugin Integration\u003C/h2\u003E\n\u003Cp\u003EAuthorization is a plugin domain.\u003C/p\u003E\n\u003Cp\u003EIt:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EDoes NOT depend on other domains\u003C/li\u003E\n\u003Cli\u003EUses contracts only\u003C/li\u003E\n\u003Cli\u003EIntegrates via policies and claims\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022key-takeaways\u0022\u003E\uD83C\uDFAF Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EAuthorization is policy-driven\u003C/li\u003E\n\u003Cli\u003ERoles are permission containers\u003C/li\u003E\n\u003Cli\u003EPermissions support wildcard \u0026amp; prefix\u003C/li\u003E\n\u003Cli\u003EPolicies enforce rules\u003C/li\u003E\n\u003Cli\u003EFully extensible and replaceable\u003C/li\u003E\n\u003C/ul\u003E\n" + "Html": "\n\u003Cp\u003EUltimateAuth provides a flexible and extensible authorization system based on:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERoles\u003C/li\u003E\n\u003Cli\u003EPermissions\u003C/li\u003E\n\u003Cli\u003EPolicies\u003C/li\u003E\n\u003Cli\u003EAccess orchestration\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022core-concepts\u0022\u003E\uD83E\uDDE9 Core Concepts\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022permissions\u0022\u003E\uD83D\uDD11 Permissions\u003C/h3\u003E\n\u003Cp\u003EIn UltimateAuth, permissions are not just arbitrary strings.\u003C/p\u003E\n\u003Cp\u003EThey follow a \u003Cstrong\u003Estructured action model\u003C/strong\u003E.\u003C/p\u003E\n\u003Ch4 id=\u0022permission-structure\u0022\u003E\uD83E\uDDE9 Permission Structure\u003C/h4\u003E\n\u003Cp\u003EPermissions are built using a consistent format:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003Eresource.operation.scope\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003Eor\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode\u003Eresource.subresource.operation.scope\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch4 id=\u0022examples\u0022\u003E\u2705 Examples\u003C/h4\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003Eusers.create.admin\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003Eusers.profile.update.self\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003Esessions.revokechain.admin\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003Ecredentials.change.self\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This structure is not accidental \u2014\u003Cbr /\u003E\nit is \u003Cstrong\u003Edesigned for consistency, readability, and policy evaluation\u003C/strong\u003E.\u003C/p\u003E\n\u003Chr /\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022built-in-action-catalog\u0022\u003E\u2699\uFE0F Built-in Action Catalog\u003C/h3\u003E\n\u003Cp\u003EUltimateAuth provides a predefined action catalog.\u003C/p\u003E\n\u003Cp\u003EExamples:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003Eflows.logout.self\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003Esessions.listchains.admin\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003Eusers.delete.self\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003Ecredentials.revoke.admin\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003Eauthorization.roles.assign.admin\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This ensures:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ENo magic strings\u003C/li\u003E\n\u003Cli\u003EDiscoverable permissions\u003C/li\u003E\n\u003Cli\u003EConsistent naming across the system\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022scope-semantics\u0022\u003E\uD83E\uDDE0 Scope Semantics\u003C/h3\u003E\n\u003Cp\u003EThe last part of the permission defines \u003Cstrong\u003Escope\u003C/strong\u003E:\u003C/p\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EScope\u003C/th\u003E\n\u003Cth\u003EMeaning\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Ccode\u003Eself\u003C/code\u003E\u003C/td\u003E\n\u003Ctd\u003EUser acts on own resources\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Ccode\u003Eadmin\u003C/code\u003E\u003C/td\u003E\n\u003Ctd\u003EUser acts on other users\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003E\u003Ccode\u003Eanonymous\u003C/code\u003E\u003C/td\u003E\n\u003Ctd\u003ENo authentication required\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Cbr\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022wildcards-grouping\u0022\u003E\uD83C\uDF32 Wildcards \u0026amp; Grouping\u003C/h3\u003E\n\u003Cp\u003EPermissions support hierarchical matching:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003Eusers.*\u003C/code\u003E \u2192 all user actions\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003Eusers.profile.*\u003C/code\u003E \u2192 all profile operations\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003E*\u003C/code\u003E \u2192 full access\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022normalization\u0022\u003E\u26A1 Normalization\u003C/h3\u003E\n\u003Cp\u003EPermissions are automatically normalized:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EFull coverage \u2192 replaced with \u003Ccode\u003E*\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003EFull group \u2192 replaced with \u003Ccode\u003Eprefix.*\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022role\u0022\u003ERole\u003C/h3\u003E\n\u003Cp\u003EA role is a collection of permissions.\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERoles are tenant-scoped\u003C/li\u003E\n\u003Cli\u003ERoles can be dynamically updated\u003C/li\u003E\n\u003Cli\u003EPermissions are normalized internally\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022userrole\u0022\u003EUserRole\u003C/h3\u003E\n\u003Cp\u003EUsers are assigned roles:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EMany-to-many relationship\u003C/li\u003E\n\u003Cli\u003EAssignment is timestamped\u003C/li\u003E\n\u003Cli\u003ERole resolution is runtime-based\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022permission-resolution\u0022\u003E\uD83D\uDD04 Permission Resolution\u003C/h2\u003E\n\u003Cp\u003EPermissions are evaluated using:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EExact match\u003C/li\u003E\n\u003Cli\u003EPrefix match\u003C/li\u003E\n\u003Cli\u003EWildcard match\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003ECompiledPermissionSet optimizes runtime checks.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022claims-integration\u0022\u003E\uD83E\uDDE0 Claims Integration\u003C/h2\u003E\n\u003Cp\u003EAuthorization integrates with authentication via claims:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERoles \u2192 \u003Ccode\u003EClaimTypes.Role\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003EPermissions \u2192 \u003Ccode\u003Epermission\u003C/code\u003E claim\u003C/li\u003E\n\u003Cli\u003ETenant \u2192 \u003Ccode\u003Etenant\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThis allows:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EToken-based authorization\u003C/li\u003E\n\u003Cli\u003EStateless permission checks (for JWT modes)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022authorization-flow\u0022\u003E\u2699\uFE0F Authorization Flow\u003C/h2\u003E\n\u003Cp\u003EAuthorization is executed through:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 AccessOrchestrator\u003C/p\u003E\n\u003Cp\u003ESteps:\u003C/p\u003E\n\u003Col\u003E\n\u003Cli\u003EBuild AccessContext\u003C/li\u003E\n\u003Cli\u003EExecute policies\u003C/li\u003E\n\u003Cli\u003EAllow or deny operation\u003C/li\u003E\n\u003C/ol\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022policies\u0022\u003E\uD83D\uDEE1 Policies\u003C/h2\u003E\n\u003Cp\u003EPolicies are the core of authorization logic.\u003C/p\u003E\n\u003Cp\u003EDefault policies include:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERequireAuthenticated\u003C/li\u003E\n\u003Cli\u003EDenyCrossTenant\u003C/li\u003E\n\u003Cli\u003ERequireActiveUser\u003C/li\u003E\n\u003Cli\u003ERequireSelf\u003C/li\u003E\n\u003Cli\u003ERequireSystem\u003C/li\u003E\n\u003Cli\u003EMustHavePermission\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022plugin-integration\u0022\u003E\uD83D\uDD0C Plugin Integration\u003C/h2\u003E\n\u003Cp\u003EAuthorization is a plugin domain.\u003C/p\u003E\n\u003Cp\u003EIt:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EDoes NOT depend on other domains\u003C/li\u003E\n\u003Cli\u003EUses contracts only\u003C/li\u003E\n\u003Cli\u003EIntegrates via policies and claims\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022key-takeaways\u0022\u003E\uD83C\uDFAF Key Takeaways\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EAuthorization is policy-driven\u003C/li\u003E\n\u003Cli\u003ERoles are permission containers\u003C/li\u003E\n\u003Cli\u003EPermissions support wildcard \u0026amp; prefix\u003C/li\u003E\n\u003Cli\u003EPolicies enforce rules\u003C/li\u003E\n\u003Cli\u003EFully extensible and replaceable\u003C/li\u003E\n\u003C/ul\u003E\n", + "Headings": [ + { + "Id": "core-concepts", + "Text": "\uD83E\uDDE9 Core Concepts", + "Level": 0 + }, + { + "Id": "permissions", + "Text": "\uD83D\uDD11 Permissions", + "Level": 1 + }, + { + "Id": "built-in-action-catalog", + "Text": "\u2699\uFE0F Built-in Action Catalog", + "Level": 1 + }, + { + "Id": "scope-semantics", + "Text": "\uD83E\uDDE0 Scope Semantics", + "Level": 1 + }, + { + "Id": "wildcards-grouping", + "Text": "\uD83C\uDF32 Wildcards \u0026 Grouping", + "Level": 1 + }, + { + "Id": "normalization", + "Text": "\u26A1 Normalization", + "Level": 1 + }, + { + "Id": "role", + "Text": "Role", + "Level": 1 + }, + { + "Id": "userrole", + "Text": "UserRole", + "Level": 1 + }, + { + "Id": "permission-resolution", + "Text": "\uD83D\uDD04 Permission Resolution", + "Level": 0 + }, + { + "Id": "claims-integration", + "Text": "\uD83E\uDDE0 Claims Integration", + "Level": 0 + }, + { + "Id": "authorization-flow", + "Text": "\u2699\uFE0F Authorization Flow", + "Level": 0 + }, + { + "Id": "policies", + "Text": "\uD83D\uDEE1 Policies", + "Level": 0 + }, + { + "Id": "plugin-integration", + "Text": "\uD83D\uDD0C Plugin Integration", + "Level": 0 + }, + { + "Id": "key-takeaways", + "Text": "\uD83C\uDFAF Key Takeaways", + "Level": 0 + } + ] } \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/credential-domain.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/credential-domain.json index 13e24a26..7852b23f 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/credential-domain.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/credential-domain.json @@ -1,5 +1,57 @@ { "Slug": "plugin-domains/credential-domain", "Title": "Credentials", - "Html": "\n\u003Cp\u003ECredentials in UltimateAuth define how a user proves their identity.\u003C/p\u003E\n\u003Ch2 id=\u0022core-concept\u0022\u003E\uD83E\uDDE0 Core Concept\u003C/h2\u003E\n\u003Cp\u003EAuthentication is not tied to users directly.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 It is performed through credentials.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022what-is-a-credential\u0022\u003E\uD83D\uDD11 What is a Credential?\u003C/h2\u003E\n\u003Cp\u003EA credential represents a secret or factor used for authentication.\u003C/p\u003E\n\u003Cp\u003EExamples:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EPassword\u003C/li\u003E\n\u003Cli\u003EOTP (future)\u003C/li\u003E\n\u003Cli\u003EExternal providers (future)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022password-credential\u0022\u003E\uD83D\uDD12 Password Credential\u003C/h2\u003E\n\u003Cp\u003EThe default credential type is password.\u003C/p\u003E\n\u003Cp\u003EA password credential contains:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EHashed secret (never raw password)\u003C/li\u003E\n\u003Cli\u003ESecurity state (active, revoked, expired)\u003C/li\u003E\n\u003Cli\u003EMetadata (last used, source, etc.)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Credentials are always stored securely and validated through hashing.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022credential-validation\u0022\u003E\u2699\uFE0F Credential Validation\u003C/h2\u003E\n\u003Cp\u003ECredential validation is handled by a validator:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EVerifies secret using hashing\u003C/li\u003E\n\u003Cli\u003EChecks credential usability (revoked, expired, etc.)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Validation is isolated from business logic.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022integration-with-users\u0022\u003E\uD83D\uDD17 Integration with Users\u003C/h2\u003E\n\u003Cp\u003ECredentials are NOT created directly inside user logic.\u003C/p\u003E\n\u003Cp\u003EInstead:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 They are integrated via lifecycle hooks\u003C/p\u003E\n\u003Cp\u003EExample:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EWhen a user is created \u2192 password credential may be created\u003C/li\u003E\n\u003Cli\u003EWhen a user is deleted \u2192 credentials are removed\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This keeps domains decoupled.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022credential-lifecycle\u0022\u003E\uD83D\uDD04 Credential Lifecycle\u003C/h2\u003E\n\u003Cp\u003ECredentials support:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECreation\u003C/li\u003E\n\u003Cli\u003ESecret change\u003C/li\u003E\n\u003Cli\u003ERevocation\u003C/li\u003E\n\u003Cli\u003EExpiration\u003C/li\u003E\n\u003Cli\u003EDeletion\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022security-behavior\u0022\u003E\uD83D\uDD01 Security Behavior\u003C/h2\u003E\n\u003Cp\u003ECredential changes trigger security actions:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EChanging password revokes sessions\u003C/li\u003E\n\u003Cli\u003EReset flows require verification tokens\u003C/li\u003E\n\u003Cli\u003EInvalid attempts are tracked\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Credentials are tightly coupled with security.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022reset-flow\u0022\u003E\uD83D\uDD11 Reset Flow\u003C/h2\u003E\n\u003Cp\u003EPassword reset is a multi-step process:\u003C/p\u003E\n\u003Col\u003E\n\u003Cli\u003EBegin reset (generate token or code)\u003C/li\u003E\n\u003Cli\u003EValidate token\u003C/li\u003E\n\u003Cli\u003EApply new secret\u003C/li\u003E\n\u003C/ol\u003E\n\u003Cp\u003E\uD83D\uDC49 Reset flow is protected against enumeration and abuse.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EUsers define identity.\u003C/p\u003E\n\u003Cp\u003ECredentials define authentication.\u003C/p\u003E\n\u003Ch2 id=\u0022summary\u0022\u003E\uD83C\uDFAF Summary\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003ECredentials handle authentication secrets\u003C/li\u003E\n\u003Cli\u003EPassword is default but extensible\u003C/li\u003E\n\u003Cli\u003EIntegrated via lifecycle hooks\u003C/li\u003E\n\u003Cli\u003EStrong security guarantees\u003C/li\u003E\n\u003Cli\u003EFully extensible for new credential types\u003C/li\u003E\n\u003C/ul\u003E\n" + "Html": "\n\u003Cp\u003ECredentials in UltimateAuth define how a user proves their identity.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022core-concept\u0022\u003E\uD83E\uDDE0 Core Concept\u003C/h2\u003E\n\u003Cp\u003EAuthentication is not tied to users directly.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 It is performed through credentials.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-is-a-credential\u0022\u003E\uD83D\uDD11 What is a Credential?\u003C/h2\u003E\n\u003Cp\u003EA credential represents a secret or factor used for authentication.\u003C/p\u003E\n\u003Cp\u003EExamples:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EPassword\u003C/li\u003E\n\u003Cli\u003EOTP (future)\u003C/li\u003E\n\u003Cli\u003EExternal providers (future)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022password-credential\u0022\u003E\uD83D\uDD12 Password Credential\u003C/h2\u003E\n\u003Cp\u003EThe default credential type is password.\u003C/p\u003E\n\u003Cp\u003EA password credential contains:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EHashed secret (never raw password)\u003C/li\u003E\n\u003Cli\u003ESecurity state (active, revoked, expired)\u003C/li\u003E\n\u003Cli\u003EMetadata (last used, source, etc.)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Credentials are always stored securely and validated through hashing.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022credential-validation\u0022\u003E\u2699\uFE0F Credential Validation\u003C/h2\u003E\n\u003Cp\u003ECredential validation is handled by a validator:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EVerifies secret using hashing\u003C/li\u003E\n\u003Cli\u003EChecks credential usability (revoked, expired, etc.)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Validation is isolated from business logic.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022integration-with-users\u0022\u003E\uD83D\uDD17 Integration with Users\u003C/h2\u003E\n\u003Cp\u003ECredentials are NOT created directly inside user logic.\u003C/p\u003E\n\u003Cp\u003EInstead:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 They are integrated via lifecycle hooks\u003C/p\u003E\n\u003Cp\u003EExample:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EWhen a user is created \u2192 password credential may be created\u003C/li\u003E\n\u003Cli\u003EWhen a user is deleted \u2192 credentials are removed\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This keeps domains decoupled.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022credential-lifecycle\u0022\u003E\uD83D\uDD04 Credential Lifecycle\u003C/h2\u003E\n\u003Cp\u003ECredentials support:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECreation\u003C/li\u003E\n\u003Cli\u003ESecret change\u003C/li\u003E\n\u003Cli\u003ERevocation\u003C/li\u003E\n\u003Cli\u003EExpiration\u003C/li\u003E\n\u003Cli\u003EDeletion\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-behavior\u0022\u003E\uD83D\uDD01 Security Behavior\u003C/h2\u003E\n\u003Cp\u003ECredential changes trigger security actions:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EChanging password revokes sessions\u003C/li\u003E\n\u003Cli\u003EReset flows require verification tokens\u003C/li\u003E\n\u003Cli\u003EInvalid attempts are tracked\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Credentials are tightly coupled with security.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022reset-flow\u0022\u003E\uD83D\uDD11 Reset Flow\u003C/h2\u003E\n\u003Cp\u003EPassword reset is a multi-step process:\u003C/p\u003E\n\u003Col\u003E\n\u003Cli\u003EBegin reset (generate token or code)\u003C/li\u003E\n\u003Cli\u003EValidate token\u003C/li\u003E\n\u003Cli\u003EApply new secret\u003C/li\u003E\n\u003C/ol\u003E\n\u003Cp\u003E\uD83D\uDC49 Reset flow is protected against enumeration and abuse.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EUsers define identity.\u003C/p\u003E\n\u003Cp\u003ECredentials define authentication.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022summary\u0022\u003E\uD83C\uDFAF Summary\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003ECredentials handle authentication secrets\u003C/li\u003E\n\u003Cli\u003EPassword is default but extensible\u003C/li\u003E\n\u003Cli\u003EIntegrated via lifecycle hooks\u003C/li\u003E\n\u003Cli\u003EStrong security guarantees\u003C/li\u003E\n\u003Cli\u003EFully extensible for new credential types\u003C/li\u003E\n\u003C/ul\u003E\n", + "Headings": [ + { + "Id": "core-concept", + "Text": "\uD83E\uDDE0 Core Concept", + "Level": 0 + }, + { + "Id": "what-is-a-credential", + "Text": "\uD83D\uDD11 What is a Credential?", + "Level": 0 + }, + { + "Id": "password-credential", + "Text": "\uD83D\uDD12 Password Credential", + "Level": 0 + }, + { + "Id": "credential-validation", + "Text": "\u2699\uFE0F Credential Validation", + "Level": 0 + }, + { + "Id": "integration-with-users", + "Text": "\uD83D\uDD17 Integration with Users", + "Level": 0 + }, + { + "Id": "credential-lifecycle", + "Text": "\uD83D\uDD04 Credential Lifecycle", + "Level": 0 + }, + { + "Id": "security-behavior", + "Text": "\uD83D\uDD01 Security Behavior", + "Level": 0 + }, + { + "Id": "reset-flow", + "Text": "\uD83D\uDD11 Reset Flow", + "Level": 0 + }, + { + "Id": "mental-model", + "Text": "\uD83E\uDDE0 Mental Model", + "Level": 0 + }, + { + "Id": "summary", + "Text": "\uD83C\uDFAF Summary", + "Level": 0 + } + ] } \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/index.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/index.json index b122602b..4ed257bd 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/index.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/index.json @@ -1,5 +1,77 @@ { "Slug": "plugin-domains/index", "Title": "Plugin Domains", - "Html": "\n\u003Cp\u003EAuthentication alone is not enough.\u003C/p\u003E\n\u003Cp\u003EReal-world systems also require:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUser management\u003C/li\u003E\n\u003Cli\u003ECredential handling\u003C/li\u003E\n\u003Cli\u003EAuthorization rules\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 UltimateAuth provides these as \u003Cstrong\u003Eplugin domains\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022what-is-a-plugin-domain\u0022\u003E\uD83E\uDDE0 What Is a Plugin Domain?\u003C/h2\u003E\n\u003Cp\u003EA plugin domain is a \u003Cstrong\u003Emodular business layer\u003C/strong\u003E built on top of UltimateAuth.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Core handles authentication\u003Cbr /\u003E\n\uD83D\uDC49 Plugin domains handle identity and access logic\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022architecture\u0022\u003E\uD83C\uDFD7 Architecture\u003C/h2\u003E\n\u003Cp\u003EEach plugin domain is composed of multiple layers:\u003C/p\u003E\n\u003Ch3 id=\u0022bridge-package\u0022\u003E\uD83D\uDD39 Bridge Package\u003C/h3\u003E\n\u003Cp\u003EDefines the server needed bridge interfaces:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 This package provides required minimal info for server package.\u003C/p\u003E\n\u003Ch3 id=\u0022contracts-package\u0022\u003E\uD83D\uDD39 Contracts Package\u003C/h3\u003E\n\u003Cp\u003EShared models between:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EServer\u003C/li\u003E\n\u003Cli\u003EClient\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Includes DTOs, requests, responses\u003C/p\u003E\n\u003Ch3 id=\u0022reference-implementation\u0022\u003E\uD83D\uDD39 Reference Implementation\u003C/h3\u003E\n\u003Cp\u003EProvides default behavior:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EApplication services\u003C/li\u003E\n\u003Cli\u003EStore interfaces\u003C/li\u003E\n\u003Cli\u003EDefault implementations\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Acts as a production-ready baseline\u003C/p\u003E\n\u003Ch3 id=\u0022persistence-layer\u0022\u003E\uD83D\uDD39 Persistence Layer\u003C/h3\u003E\n\u003Cp\u003EProvides storage implementations:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EInMemory\u003C/li\u003E\n\u003Cli\u003EEntity Framework Core\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Additional providers (Redis, etc.) can be added\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022extensibility-model\u0022\u003E\uD83D\uDD04 Extensibility Model\u003C/h2\u003E\n\u003Cp\u003EPlugin domains are designed to be:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EReplaceable\u003C/li\u003E\n\u003Cli\u003EExtendable\u003C/li\u003E\n\u003Cli\u003EComposable\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You can implement your own persistence\u003Cbr /\u003E\n\uD83D\uDC49 You can extend behavior\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022recommended-approach\u0022\u003E\u26A0\uFE0F Recommended Approach\u003C/h2\u003E\n\u003Cp\u003EIn most cases:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 You should NOT replace a plugin domain entirely\u003C/p\u003E\n\u003Cp\u003EInstead:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUse provided implementations\u003C/li\u003E\n\u003Cli\u003EExtend via interfaces\u003C/li\u003E\n\u003Cli\u003ECustomize behavior where needed\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This ensures compatibility with the framework\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022domain-isolation\u0022\u003E\uD83D\uDD0C Domain Isolation\u003C/h2\u003E\n\u003Cp\u003EPlugin domains are designed to be \u003Cstrong\u003Efully isolated\u003C/strong\u003E from each other.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 A plugin domain does NOT reference another plugin domain\u003Cbr /\u003E\n\uD83D\uDC49 There are no direct dependencies between domains\u003C/p\u003E\n\u003Ch3 id=\u0022why\u0022\u003E\uD83E\uDDE0 Why?\u003C/h3\u003E\n\u003Cp\u003EThis ensures:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELoose coupling\u003C/li\u003E\n\u003Cli\u003EIndependent evolution\u003C/li\u003E\n\u003Cli\u003EReplaceability\u003C/li\u003E\n\u003Cli\u003EClear boundaries\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022communication-via-hooks\u0022\u003E\uD83D\uDD04 Communication via Hooks\u003C/h2\u003E\n\u003Cp\u003EPlugin domains communicate \u003Cstrong\u003Eonly through integration hooks\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EFor example:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUser domain triggers \u2192 \u003Ccode\u003EOnUserCreated\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003ECredential domain listens \u2192 creates password credential\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This is implemented via abstractions such as:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003EIUserLifecycleIntegration\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003Edomain events / integration points\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Core = authentication engine\u003Cbr /\u003E\n\uD83D\uDC49 Plugin domains = business logic\u003C/p\u003E\n\u003Ch2 id=\u0022why-this-matters\u0022\u003E\uD83C\uDFAF Why This Matters\u003C/h2\u003E\n\u003Cp\u003EThis architecture allows UltimateAuth to:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EStay modular\u003C/li\u003E\n\u003Cli\u003ESupport multiple domains\u003C/li\u003E\n\u003Cli\u003EEnable enterprise customization\u003C/li\u003E\n\u003Cli\u003EAvoid monolithic identity systems\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You don\u2019t build everything from scratch\u003Cbr /\u003E\n\uD83D\uDC49 You assemble what you need\u003C/p\u003E\n\u003Ch2 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EManage users \u2192 Users\u003C/li\u003E\n\u003Cli\u003EHandle credentials \u2192 Credentials\u003C/li\u003E\n\u003Cli\u003EControl access \u2192 Authorization\u003C/li\u003E\n\u003C/ul\u003E\n" + "Html": "\n\u003Cp\u003EAuthentication alone is not enough.\u003C/p\u003E\n\u003Cp\u003EReal-world systems also require:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUser management\u003C/li\u003E\n\u003Cli\u003ECredential handling\u003C/li\u003E\n\u003Cli\u003EAuthorization rules\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 UltimateAuth provides these as \u003Cstrong\u003Eplugin domains\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-is-a-plugin-domain\u0022\u003E\uD83E\uDDE0 What Is a Plugin Domain?\u003C/h2\u003E\n\u003Cp\u003EA plugin domain is a \u003Cstrong\u003Emodular business layer\u003C/strong\u003E built on top of UltimateAuth.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Core handles authentication\u003Cbr /\u003E\n\uD83D\uDC49 Plugin domains handle identity and access logic\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022architecture\u0022\u003E\uD83C\uDFD7 Architecture\u003C/h2\u003E\n\u003Cp\u003EEach plugin domain is composed of multiple layers:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022bridge-package\u0022\u003E\uD83D\uDD39 Bridge Package\u003C/h3\u003E\n\u003Cp\u003EDefines the server needed bridge interfaces:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 This package provides required minimal info for server package.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022contracts-package\u0022\u003E\uD83D\uDD39 Contracts Package\u003C/h3\u003E\n\u003Cp\u003EShared models between:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EServer\u003C/li\u003E\n\u003Cli\u003EClient\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Includes DTOs, requests, responses\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022reference-implementation\u0022\u003E\uD83D\uDD39 Reference Implementation\u003C/h3\u003E\n\u003Cp\u003EProvides default behavior:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EApplication services\u003C/li\u003E\n\u003Cli\u003EStore interfaces\u003C/li\u003E\n\u003Cli\u003EDefault implementations\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Acts as a production-ready baseline\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022persistence-layer\u0022\u003E\uD83D\uDD39 Persistence Layer\u003C/h3\u003E\n\u003Cp\u003EProvides storage implementations:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EInMemory\u003C/li\u003E\n\u003Cli\u003EEntity Framework Core\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Additional providers (Redis, etc.) can be added\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022extensibility-model\u0022\u003E\uD83D\uDD04 Extensibility Model\u003C/h2\u003E\n\u003Cp\u003EPlugin domains are designed to be:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EReplaceable\u003C/li\u003E\n\u003Cli\u003EExtendable\u003C/li\u003E\n\u003Cli\u003EComposable\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You can implement your own persistence\u003Cbr /\u003E\n\uD83D\uDC49 You can extend behavior\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022recommended-approach\u0022\u003E\u26A0\uFE0F Recommended Approach\u003C/h2\u003E\n\u003Cp\u003EIn most cases:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 You should NOT replace a plugin domain entirely\u003C/p\u003E\n\u003Cp\u003EInstead:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUse provided implementations\u003C/li\u003E\n\u003Cli\u003EExtend via interfaces\u003C/li\u003E\n\u003Cli\u003ECustomize behavior where needed\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This ensures compatibility with the framework\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022domain-isolation\u0022\u003E\uD83D\uDD0C Domain Isolation\u003C/h2\u003E\n\u003Cp\u003EPlugin domains are designed to be \u003Cstrong\u003Efully isolated\u003C/strong\u003E from each other.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 A plugin domain does NOT reference another plugin domain\u003Cbr /\u003E\n\uD83D\uDC49 There are no direct dependencies between domains\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022why\u0022\u003E\uD83E\uDDE0 Why?\u003C/h3\u003E\n\u003Cp\u003EThis ensures:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELoose coupling\u003C/li\u003E\n\u003Cli\u003EIndependent evolution\u003C/li\u003E\n\u003Cli\u003EReplaceability\u003C/li\u003E\n\u003Cli\u003EClear boundaries\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022communication-via-hooks\u0022\u003E\uD83D\uDD04 Communication via Hooks\u003C/h2\u003E\n\u003Cp\u003EPlugin domains communicate \u003Cstrong\u003Eonly through integration hooks\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EFor example:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUser domain triggers \u2192 \u003Ccode\u003EOnUserCreated\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003ECredential domain listens \u2192 creates password credential\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This is implemented via abstractions such as:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003EIUserLifecycleIntegration\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003Edomain events / integration points\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Core = authentication engine\u003Cbr /\u003E\n\uD83D\uDC49 Plugin domains = business logic\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022why-this-matters\u0022\u003E\uD83C\uDFAF Why This Matters\u003C/h2\u003E\n\u003Cp\u003EThis architecture allows UltimateAuth to:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EStay modular\u003C/li\u003E\n\u003Cli\u003ESupport multiple domains\u003C/li\u003E\n\u003Cli\u003EEnable enterprise customization\u003C/li\u003E\n\u003Cli\u003EAvoid monolithic identity systems\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 You don\u2019t build everything from scratch\u003Cbr /\u003E\n\uD83D\uDC49 You assemble what you need\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EManage users \u2192 Users\u003C/li\u003E\n\u003Cli\u003EHandle credentials \u2192 Credentials\u003C/li\u003E\n\u003Cli\u003EControl access \u2192 Authorization\u003C/li\u003E\n\u003C/ul\u003E\n", + "Headings": [ + { + "Id": "what-is-a-plugin-domain", + "Text": "\uD83E\uDDE0 What Is a Plugin Domain?", + "Level": 0 + }, + { + "Id": "architecture", + "Text": "\uD83C\uDFD7 Architecture", + "Level": 0 + }, + { + "Id": "bridge-package", + "Text": "\uD83D\uDD39 Bridge Package", + "Level": 1 + }, + { + "Id": "contracts-package", + "Text": "\uD83D\uDD39 Contracts Package", + "Level": 1 + }, + { + "Id": "reference-implementation", + "Text": "\uD83D\uDD39 Reference Implementation", + "Level": 1 + }, + { + "Id": "persistence-layer", + "Text": "\uD83D\uDD39 Persistence Layer", + "Level": 1 + }, + { + "Id": "extensibility-model", + "Text": "\uD83D\uDD04 Extensibility Model", + "Level": 0 + }, + { + "Id": "recommended-approach", + "Text": "\u26A0\uFE0F Recommended Approach", + "Level": 0 + }, + { + "Id": "domain-isolation", + "Text": "\uD83D\uDD0C Domain Isolation", + "Level": 0 + }, + { + "Id": "why", + "Text": "\uD83E\uDDE0 Why?", + "Level": 1 + }, + { + "Id": "communication-via-hooks", + "Text": "\uD83D\uDD04 Communication via Hooks", + "Level": 0 + }, + { + "Id": "mental-model", + "Text": "\uD83E\uDDE0 Mental Model", + "Level": 0 + }, + { + "Id": "why-this-matters", + "Text": "\uD83C\uDFAF Why This Matters", + "Level": 0 + }, + { + "Id": "next-step", + "Text": "\u27A1\uFE0F Next Step", + "Level": 0 + } + ] } \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/policies.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/policies.json index a02f6401..75b0e118 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/policies.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/policies.json @@ -1,5 +1,77 @@ { "Slug": "plugin-domains/policies", "Title": "Policies", - "Html": "\n\u003Cp\u003EUltimateAuth uses a \u003Cstrong\u003Epolicy-driven authorization model\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EPolicies are not simple checks \u2014\u003Cbr /\u003E\nthey are \u003Cstrong\u003Ecomposable decision units\u003C/strong\u003E evaluated at runtime.\u003C/p\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EAuthorization in UltimateAuth is:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Context-based\u003Cbr /\u003E\n\uD83D\uDC49 Policy-driven\u003Cbr /\u003E\n\uD83D\uDC49 Orchestrated\u003C/p\u003E\n\u003Ch3 id=\u0022flow\u0022\u003EFlow\u003C/h3\u003E\n\u003Col\u003E\n\u003Cli\u003EBuild \u003Ccode\u003EAccessContext\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003EResolve policies\u003C/li\u003E\n\u003Cli\u003EExecute authority\u003C/li\u003E\n\u003Cli\u003EAllow / Deny / Reauth\u003C/li\u003E\n\u003C/ol\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022accesscontext\u0022\u003E\u2699\uFE0F AccessContext\u003C/h2\u003E\n\u003Cp\u003EEvery authorization decision is based on:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EActor (who is calling)\u003C/li\u003E\n\u003Cli\u003ETarget (what is being accessed)\u003C/li\u003E\n\u003Cli\u003EAction (what is being done)\u003C/li\u003E\n\u003Cli\u003ETenant\u003C/li\u003E\n\u003Cli\u003EClaims / permissions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022policy-resolution\u0022\u003E\uD83D\uDD0C Policy Resolution\u003C/h2\u003E\n\u003Cp\u003EPolicies are resolved using:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAction prefix matching\u003C/li\u003E\n\u003Cli\u003ERuntime filtering (\u003Ccode\u003EAppliesTo\u003C/code\u003E)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EExample:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003Eusers.create.admin\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003Eusers.*\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003Eauthorization.roles.*\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022policy-types\u0022\u003E\uD83E\uDDE9 Policy Types\u003C/h2\u003E\n\u003Ch3 id=\u0022global-policies\u0022\u003EGlobal Policies\u003C/h3\u003E\n\u003Cp\u003EAlways evaluated:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERequireAuthenticated\u003C/li\u003E\n\u003Cli\u003EDenyCrossTenant\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022runtime-policies\u0022\u003ERuntime Policies\u003C/h3\u003E\n\u003Cp\u003EResolved dynamically:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERequireActiveUser\u003C/li\u003E\n\u003Cli\u003EMustHavePermission\u003C/li\u003E\n\u003Cli\u003ERequireSelf\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022invariants\u0022\u003EInvariants\u003C/h3\u003E\n\u003Cp\u003EExecuted first:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECannot be bypassed\u003C/li\u003E\n\u003Cli\u003EHard security rules\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022policy-evaluation\u0022\u003E\u2696\uFE0F Policy Evaluation\u003C/h2\u003E\n\u003Cp\u003EEvaluation order:\u003C/p\u003E\n\u003Col\u003E\n\u003Cli\u003EInvariants\u003C/li\u003E\n\u003Cli\u003EGlobal policies\u003C/li\u003E\n\u003Cli\u003ERuntime policies\u003C/li\u003E\n\u003C/ol\u003E\n\u003Cp\u003E\uD83D\uDC49 First deny wins\u003Cbr /\u003E\n\uD83D\uDC49 Allow means \u201Cno objection\u201D\u003Cbr /\u003E\n\uD83D\uDC49 Reauth can be requested\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022example-policy\u0022\u003E\uD83D\uDD10 Example Policy\u003C/h2\u003E\n\u003Ch3 id=\u0022deny-admin-self-modification\u0022\u003EDeny Admin Self Modification\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EBlocks admin modifying own account\u003C/li\u003E\n\u003Cli\u003EApplies only to \u003Ccode\u003E.admin\u003C/code\u003E actions\u003C/li\u003E\n\u003Cli\u003EIgnores read operations\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022require-active-user\u0022\u003ERequire Active User\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EEnsures user exists\u003C/li\u003E\n\u003Cli\u003EEnsures user is active\u003C/li\u003E\n\u003Cli\u003ESkips anonymous actions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022access-orchestrator\u0022\u003E\uD83D\uDE80 Access Orchestrator\u003C/h2\u003E\n\u003Cp\u003EThe orchestrator is the entry point:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEnriches context (claims, permissions)\u003C/li\u003E\n\u003Cli\u003EResolves policies\u003C/li\u003E\n\u003Cli\u003EExecutes authority\u003C/li\u003E\n\u003Cli\u003ERuns command if allowed\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022key-principles\u0022\u003E\uD83C\uDFAF Key Principles\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EPolicies are composable\u003C/li\u003E\n\u003Cli\u003EAuthorization is deterministic\u003C/li\u003E\n\u003Cli\u003ENo hidden magic\u003C/li\u003E\n\u003Cli\u003EFully extensible\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Cp\u003E\uD83D\uDC49 Authorization is not a single check\u003Cbr /\u003E\n\uD83D\uDC49 It is a \u003Cstrong\u003Epipeline of decisions\u003C/strong\u003E\u003C/p\u003E\n" + "Html": "\n\u003Cp\u003EUltimateAuth uses a \u003Cstrong\u003Epolicy-driven authorization model\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EPolicies are not simple checks \u2014\u003Cbr /\u003E\nthey are \u003Cstrong\u003Ecomposable decision units\u003C/strong\u003E evaluated at runtime.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EAuthorization in UltimateAuth is:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Context-based\u003Cbr /\u003E\n\uD83D\uDC49 Policy-driven\u003Cbr /\u003E\n\uD83D\uDC49 Orchestrated\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022flow\u0022\u003EFlow\u003C/h3\u003E\n\u003Col\u003E\n\u003Cli\u003EBuild \u003Ccode\u003EAccessContext\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003EResolve policies\u003C/li\u003E\n\u003Cli\u003EExecute authority\u003C/li\u003E\n\u003Cli\u003EAllow / Deny / Reauth\u003C/li\u003E\n\u003C/ol\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022accesscontext\u0022\u003E\u2699\uFE0F AccessContext\u003C/h2\u003E\n\u003Cp\u003EEvery authorization decision is based on:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EActor (who is calling)\u003C/li\u003E\n\u003Cli\u003ETarget (what is being accessed)\u003C/li\u003E\n\u003Cli\u003EAction (what is being done)\u003C/li\u003E\n\u003Cli\u003ETenant\u003C/li\u003E\n\u003Cli\u003EClaims / permissions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022policy-resolution\u0022\u003E\uD83D\uDD0C Policy Resolution\u003C/h2\u003E\n\u003Cp\u003EPolicies are resolved using:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAction prefix matching\u003C/li\u003E\n\u003Cli\u003ERuntime filtering (\u003Ccode\u003EAppliesTo\u003C/code\u003E)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EExample:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u003Ccode\u003Eusers.create.admin\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003Eusers.*\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Ccode\u003Eauthorization.roles.*\u003C/code\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022policy-types\u0022\u003E\uD83E\uDDE9 Policy Types\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022global-policies\u0022\u003EGlobal Policies\u003C/h3\u003E\n\u003Cp\u003EAlways evaluated:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERequireAuthenticated\u003C/li\u003E\n\u003Cli\u003EDenyCrossTenant\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022runtime-policies\u0022\u003ERuntime Policies\u003C/h3\u003E\n\u003Cp\u003EResolved dynamically:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERequireActiveUser\u003C/li\u003E\n\u003Cli\u003EMustHavePermission\u003C/li\u003E\n\u003Cli\u003ERequireSelf\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022invariants\u0022\u003EInvariants\u003C/h3\u003E\n\u003Cp\u003EExecuted first:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECannot be bypassed\u003C/li\u003E\n\u003Cli\u003EHard security rules\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022policy-evaluation\u0022\u003E\u2696\uFE0F Policy Evaluation\u003C/h2\u003E\n\u003Cp\u003EEvaluation order:\u003C/p\u003E\n\u003Col\u003E\n\u003Cli\u003EInvariants\u003C/li\u003E\n\u003Cli\u003EGlobal policies\u003C/li\u003E\n\u003Cli\u003ERuntime policies\u003C/li\u003E\n\u003C/ol\u003E\n\u003Cp\u003E\uD83D\uDC49 First deny wins\u003Cbr /\u003E\n\uD83D\uDC49 Allow means \u201Cno objection\u201D\u003Cbr /\u003E\n\uD83D\uDC49 Reauth can be requested\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022example-policy\u0022\u003E\uD83D\uDD10 Example Policy\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022deny-admin-self-modification\u0022\u003EDeny Admin Self Modification\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EBlocks admin modifying own account\u003C/li\u003E\n\u003Cli\u003EApplies only to \u003Ccode\u003E.admin\u003C/code\u003E actions\u003C/li\u003E\n\u003Cli\u003EIgnores read operations\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022require-active-user\u0022\u003ERequire Active User\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EEnsures user exists\u003C/li\u003E\n\u003Cli\u003EEnsures user is active\u003C/li\u003E\n\u003Cli\u003ESkips anonymous actions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022access-orchestrator\u0022\u003E\uD83D\uDE80 Access Orchestrator\u003C/h2\u003E\n\u003Cp\u003EThe orchestrator is the entry point:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEnriches context (claims, permissions)\u003C/li\u003E\n\u003Cli\u003EResolves policies\u003C/li\u003E\n\u003Cli\u003EExecutes authority\u003C/li\u003E\n\u003Cli\u003ERuns command if allowed\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022key-principles\u0022\u003E\uD83C\uDFAF Key Principles\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EPolicies are composable\u003C/li\u003E\n\u003Cli\u003EAuthorization is deterministic\u003C/li\u003E\n\u003Cli\u003ENo hidden magic\u003C/li\u003E\n\u003Cli\u003EFully extensible\u003C/li\u003E\n\u003C/ul\u003E\n\u003Chr /\u003E\n\u003Cp\u003E\uD83D\uDC49 Authorization is not a single check\u003Cbr /\u003E\n\uD83D\uDC49 It is a \u003Cstrong\u003Epipeline of decisions\u003C/strong\u003E\u003C/p\u003E\n", + "Headings": [ + { + "Id": "mental-model", + "Text": "\uD83E\uDDE0 Mental Model", + "Level": 0 + }, + { + "Id": "flow", + "Text": "Flow", + "Level": 1 + }, + { + "Id": "accesscontext", + "Text": "\u2699\uFE0F AccessContext", + "Level": 0 + }, + { + "Id": "policy-resolution", + "Text": "\uD83D\uDD0C Policy Resolution", + "Level": 0 + }, + { + "Id": "policy-types", + "Text": "\uD83E\uDDE9 Policy Types", + "Level": 0 + }, + { + "Id": "global-policies", + "Text": "Global Policies", + "Level": 1 + }, + { + "Id": "runtime-policies", + "Text": "Runtime Policies", + "Level": 1 + }, + { + "Id": "invariants", + "Text": "Invariants", + "Level": 1 + }, + { + "Id": "policy-evaluation", + "Text": "\u2696\uFE0F Policy Evaluation", + "Level": 0 + }, + { + "Id": "example-policy", + "Text": "\uD83D\uDD10 Example Policy", + "Level": 0 + }, + { + "Id": "deny-admin-self-modification", + "Text": "Deny Admin Self Modification", + "Level": 1 + }, + { + "Id": "require-active-user", + "Text": "Require Active User", + "Level": 1 + }, + { + "Id": "access-orchestrator", + "Text": "\uD83D\uDE80 Access Orchestrator", + "Level": 0 + }, + { + "Id": "key-principles", + "Text": "\uD83C\uDFAF Key Principles", + "Level": 0 + } + ] } \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/users-domain.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/users-domain.json index 188e1090..03aa54a2 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/users-domain.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/plugin-domains/users-domain.json @@ -1,5 +1,47 @@ { "Slug": "plugin-domains/users-domain", "Title": "Users", - "Html": "\n\u003Cp\u003EUsers in UltimateAuth are not a single entity.\u003C/p\u003E\n\u003Cp\u003EInstead, they are composed of multiple parts that together define identity.\u003C/p\u003E\n\u003Ch2 id=\u0022core-concept\u0022\u003E\uD83E\uDDE0 Core Concept\u003C/h2\u003E\n\u003Cp\u003EA user is represented by three main components:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELifecycle (security \u0026amp; state)\u003C/li\u003E\n\u003Cli\u003EIdentifiers (login surface)\u003C/li\u003E\n\u003Cli\u003EProfile (user data)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022lifecycle-security-anchor\u0022\u003E\uD83D\uDD10 Lifecycle (Security Anchor)\u003C/h2\u003E\n\u003Cp\u003ELifecycle defines:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EWhen a user created\u003C/li\u003E\n\u003Cli\u003EWhether a user is active, suspended, or deleted\u003C/li\u003E\n\u003Cli\u003ESecurity version (used for invalidating sessions/tokens)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This is the core of authentication security.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022identifiers-login-system\u0022\u003E\uD83D\uDD11 Identifiers (Login System)\u003C/h2\u003E\n\u003Cp\u003EIdentifiers represent how a user logs in:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEmail\u003C/li\u003E\n\u003Cli\u003EUsername\u003C/li\u003E\n\u003Cli\u003EPhone\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EEach identifier has:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ENormalized value\u003C/li\u003E\n\u003Cli\u003EPrimary flag\u003C/li\u003E\n\u003Cli\u003EVerification state\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022login-identifiers\u0022\u003E\u2B50 Login Identifiers\u003C/h3\u003E\n\u003Cp\u003ENot all identifiers are used for login.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Only \u003Cstrong\u003Eprimary identifiers\u003C/strong\u003E are considered login identifiers.\u003C/p\u003E\n\u003Ch4 id=\u0022configurable-behavior\u0022\u003E\u2699\uFE0F Configurable Behavior\u003C/h4\u003E\n\u003Cp\u003EDevelopers can control:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EWhich identifier types are allowed for login\u003C/li\u003E\n\u003Cli\u003EWhether email/phone must be verified\u003C/li\u003E\n\u003Cli\u003EWhether multiple identifiers are allowed\u003C/li\u003E\n\u003Cli\u003EGlobal uniqueness rules\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch4 id=\u0022custom-login-logic\u0022\u003E\uD83D\uDD0C Custom Login Logic\u003C/h4\u003E\n\u003Cp\u003EUltimateAuth allows custom login identifier resolution.\u003C/p\u003E\n\u003Cp\u003EYou can:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAdd custom identifier types\u003C/li\u003E\n\u003Cli\u003EOverride resolution logic\u003C/li\u003E\n\u003Cli\u003EImplement your own resolver\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This means login is fully extensible.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022profile-user-data\u0022\u003E\uD83E\uDDFE Profile (User Data)\u003C/h2\u003E\n\u003Cp\u003EProfile contains non-auth data:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EName\u003C/li\u003E\n\u003Cli\u003EBio\u003C/li\u003E\n\u003Cli\u003ELocalization\u003C/li\u003E\n\u003Cli\u003EMetadata\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This is not used for authentication.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022application-service\u0022\u003E\u2699\uFE0F Application Service\u003C/h2\u003E\n\u003Cp\u003EUser operations are handled by an orchestration layer.\u003C/p\u003E\n\u003Cp\u003EIt is responsible for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECreating users\u003C/li\u003E\n\u003Cli\u003EManaging identifiers\u003C/li\u003E\n\u003Cli\u003EApplying validation rules\u003C/li\u003E\n\u003Cli\u003ETriggering integrations\u003C/li\u003E\n\u003Cli\u003ERevoking sessions when needed\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83D\uDD01 Mental Model\u003C/h2\u003E\n\u003Cp\u003EAuthentication answers:\u003C/p\u003E\n\u003Cp\u003E\u2192 Who are you?\u003C/p\u003E\n\u003Cp\u003EUsers domain answers:\u003C/p\u003E\n\u003Cp\u003E\u2192 What is this user?\u003C/p\u003E\n\u003Ch2 id=\u0022summary\u0022\u003E\uD83C\uDFAF Summary\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EUsers are composed, not singular\u003C/li\u003E\n\u003Cli\u003ELogin is based on identifiers\u003C/li\u003E\n\u003Cli\u003ELogin identifiers are configurable\u003C/li\u003E\n\u003Cli\u003ESystem is extensible by design\u003C/li\u003E\n\u003C/ul\u003E\n" + "Html": "\n\u003Cp\u003EUsers in UltimateAuth are not a single entity.\u003C/p\u003E\n\u003Cp\u003EInstead, they are composed of multiple parts that together define identity.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022core-concept\u0022\u003E\uD83E\uDDE0 Core Concept\u003C/h2\u003E\n\u003Cp\u003EA user is represented by three main components:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELifecycle (security \u0026amp; state)\u003C/li\u003E\n\u003Cli\u003EIdentifiers (login surface)\u003C/li\u003E\n\u003Cli\u003EProfile (user data)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022lifecycle-security-anchor\u0022\u003E\uD83D\uDD10 Lifecycle (Security Anchor)\u003C/h2\u003E\n\u003Cp\u003ELifecycle defines:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EWhen a user created\u003C/li\u003E\n\u003Cli\u003EWhether a user is active, suspended, or deleted\u003C/li\u003E\n\u003Cli\u003ESecurity version (used for invalidating sessions/tokens)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This is the core of authentication security.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022identifiers-login-system\u0022\u003E\uD83D\uDD11 Identifiers (Login System)\u003C/h2\u003E\n\u003Cp\u003EIdentifiers represent how a user logs in:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EEmail\u003C/li\u003E\n\u003Cli\u003EUsername\u003C/li\u003E\n\u003Cli\u003EPhone\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EEach identifier has:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ENormalized value\u003C/li\u003E\n\u003Cli\u003EPrimary flag\u003C/li\u003E\n\u003Cli\u003EVerification state\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022login-identifiers\u0022\u003E\u2B50 Login Identifiers\u003C/h3\u003E\n\u003Cp\u003ENot all identifiers are used for login.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Only \u003Cstrong\u003Eprimary identifiers\u003C/strong\u003E are considered login identifiers.\u003C/p\u003E\n\u003Ch4 id=\u0022configurable-behavior\u0022\u003E\u2699\uFE0F Configurable Behavior\u003C/h4\u003E\n\u003Cp\u003EDevelopers can control:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EWhich identifier types are allowed for login\u003C/li\u003E\n\u003Cli\u003EWhether email/phone must be verified\u003C/li\u003E\n\u003Cli\u003EWhether multiple identifiers are allowed\u003C/li\u003E\n\u003Cli\u003EGlobal uniqueness rules\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch4 id=\u0022custom-login-logic\u0022\u003E\uD83D\uDD0C Custom Login Logic\u003C/h4\u003E\n\u003Cp\u003EUltimateAuth allows custom login identifier resolution.\u003C/p\u003E\n\u003Cp\u003EYou can:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAdd custom identifier types\u003C/li\u003E\n\u003Cli\u003EOverride resolution logic\u003C/li\u003E\n\u003Cli\u003EImplement your own resolver\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This means login is fully extensible.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022profile-user-data\u0022\u003E\uD83E\uDDFE Profile (User Data)\u003C/h2\u003E\n\u003Cp\u003EProfile contains non-auth data:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EName\u003C/li\u003E\n\u003Cli\u003EBio\u003C/li\u003E\n\u003Cli\u003ELocalization\u003C/li\u003E\n\u003Cli\u003EMetadata\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This is not used for authentication.\u003C/p\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022application-service\u0022\u003E\u2699\uFE0F Application Service\u003C/h2\u003E\n\u003Cp\u003EUser operations are handled by an orchestration layer.\u003C/p\u003E\n\u003Cp\u003EIt is responsible for:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECreating users\u003C/li\u003E\n\u003Cli\u003EManaging identifiers\u003C/li\u003E\n\u003Cli\u003EApplying validation rules\u003C/li\u003E\n\u003Cli\u003ETriggering integrations\u003C/li\u003E\n\u003Cli\u003ERevoking sessions when needed\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83D\uDD01 Mental Model\u003C/h2\u003E\n\u003Cp\u003EAuthentication answers:\u003C/p\u003E\n\u003Cp\u003E\u2192 Who are you?\u003C/p\u003E\n\u003Cp\u003EUsers domain answers:\u003C/p\u003E\n\u003Cp\u003E\u2192 What is this user?\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022summary\u0022\u003E\uD83C\uDFAF Summary\u003C/h2\u003E\n\u003Cul\u003E\n\u003Cli\u003EUsers are composed, not singular\u003C/li\u003E\n\u003Cli\u003ELogin is based on identifiers\u003C/li\u003E\n\u003Cli\u003ELogin identifiers are configurable\u003C/li\u003E\n\u003Cli\u003ESystem is extensible by design\u003C/li\u003E\n\u003C/ul\u003E\n", + "Headings": [ + { + "Id": "core-concept", + "Text": "\uD83E\uDDE0 Core Concept", + "Level": 0 + }, + { + "Id": "lifecycle-security-anchor", + "Text": "\uD83D\uDD10 Lifecycle (Security Anchor)", + "Level": 0 + }, + { + "Id": "identifiers-login-system", + "Text": "\uD83D\uDD11 Identifiers (Login System)", + "Level": 0 + }, + { + "Id": "login-identifiers", + "Text": "\u2B50 Login Identifiers", + "Level": 1 + }, + { + "Id": "profile-user-data", + "Text": "\uD83E\uDDFE Profile (User Data)", + "Level": 0 + }, + { + "Id": "application-service", + "Text": "\u2699\uFE0F Application Service", + "Level": 0 + }, + { + "Id": "mental-model", + "Text": "\uD83D\uDD01 Mental Model", + "Level": 0 + }, + { + "Id": "summary", + "Text": "\uD83C\uDFAF Summary", + "Level": 0 + } + ] } \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/readme.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/readme.json index cdbd470c..98a6eac6 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/readme.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/readme.json @@ -1,5 +1,47 @@ { "Slug": "readme", "Title": "\uD83D\uDD10 UltimateAuth Docs", - "Html": "\n\u003Cp\u003EThe modern, unified auth framework with platform-level abilities for .NET - Reimagined.\u003C/p\u003E\n\u003Ch2 id=\u0022sections\u0022\u003ESections\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth docs consists of 7 sections. There are suggested read line for starters:\u003C/p\u003E\n\u003Ch3 id=\u0022getting-started\u0022\u003E1) Getting Started\u003C/h3\u003E\n\u003Cp\u003EStart here to integrate UltimateAuth into your application:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EGetting Started Guide\u003C/li\u003E\n\u003Cli\u003ESetup with different client profiles\u003C/li\u003E\n\u003Cli\u003EBasic usage\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022fundamentals\u0022\u003E2) Fundamentals\u003C/h3\u003E\n\u003Cp\u003EUnderstand how UltimateAuth works internally:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECore Concepts\u003C/li\u003E\n\u003Cli\u003ESession Architecture (Root \u2192 Chain \u2192 Session)\u003C/li\u003E\n\u003Cli\u003EToken Model\u003C/li\u003E\n\u003Cli\u003EAuthorization Model\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022auth-flows\u0022\u003E3) Auth Flows\u003C/h3\u003E\n\u003Cp\u003EExplore authentication lifecycle:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogin Flow\u003C/li\u003E\n\u003Cli\u003ERefresh Flow\u003C/li\u003E\n\u003Cli\u003ELogout Flow\u003C/li\u003E\n\u003Cli\u003ESession Lifecycle\u003C/li\u003E\n\u003Cli\u003EToken Behavior\u003C/li\u003E\n\u003Cli\u003EDevice Management\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022plugin-domains\u0022\u003E4) Plugin Domains\u003C/h3\u003E\n\u003Cp\u003EUltimateAuth is built as modular domains:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUsers\u003C/li\u003E\n\u003Cli\u003ECredentials\u003C/li\u003E\n\u003Cli\u003EAuthorization\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022configuration-extensibility\u0022\u003E5) Configuration \u0026amp; Extensibility\u003C/h3\u003E\n\u003Cp\u003ECustomize behavior and integrate with your system:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EServer Options\u003C/li\u003E\n\u003Cli\u003EClient Options\u003C/li\u003E\n\u003Cli\u003EConfiguration Sources\u003C/li\u003E\n\u003Cli\u003EAdvanced Configuration\u003C/li\u003E\n\u003Cli\u003EExtending Domains \u0026amp; Stores\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022client-api\u0022\u003E6) Client \u0026amp; API\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EReal world usage of UltimateAuth\u003C/li\u003E\n\u003Cli\u003EAPI and code examples of authentication, sessions, users, credentials and authorization\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022security-advanced\u0022\u003E7) Security \u0026amp; Advanced\u003C/h3\u003E\n\u003Cp\u003EDeep dive into the security model:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession Security Model\u003C/li\u003E\n\u003Cli\u003ERefresh Token Rotation\u003C/li\u003E\n\u003Cli\u003EAccess Token Behavior\u003C/li\u003E\n\u003Cli\u003EPolicy Pipeline\u003C/li\u003E\n\u003C/ul\u003E\n" + "Html": "\n\u003Cp\u003EThe modern, unified auth framework with platform-level abilities for .NET - Reimagined.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022sections\u0022\u003ESections\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth docs consists of 7 sections. There are suggested read line for starters:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022getting-started\u0022\u003E1) Getting Started\u003C/h3\u003E\n\u003Cp\u003EStart here to integrate UltimateAuth into your application:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EGetting Started Guide\u003C/li\u003E\n\u003Cli\u003ESetup with different client profiles\u003C/li\u003E\n\u003Cli\u003EBasic usage\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022fundamentals\u0022\u003E2) Fundamentals\u003C/h3\u003E\n\u003Cp\u003EUnderstand how UltimateAuth works internally:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECore Concepts\u003C/li\u003E\n\u003Cli\u003ESession Architecture (Root \u2192 Chain \u2192 Session)\u003C/li\u003E\n\u003Cli\u003EToken Model\u003C/li\u003E\n\u003Cli\u003EAuthorization Model\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022auth-flows\u0022\u003E3) Auth Flows\u003C/h3\u003E\n\u003Cp\u003EExplore authentication lifecycle:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ELogin Flow\u003C/li\u003E\n\u003Cli\u003ERefresh Flow\u003C/li\u003E\n\u003Cli\u003ELogout Flow\u003C/li\u003E\n\u003Cli\u003ESession Lifecycle\u003C/li\u003E\n\u003Cli\u003EToken Behavior\u003C/li\u003E\n\u003Cli\u003EDevice Management\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022plugin-domains\u0022\u003E4) Plugin Domains\u003C/h3\u003E\n\u003Cp\u003EUltimateAuth is built as modular domains:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUsers\u003C/li\u003E\n\u003Cli\u003ECredentials\u003C/li\u003E\n\u003Cli\u003EAuthorization\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022configuration-extensibility\u0022\u003E5) Configuration \u0026amp; Extensibility\u003C/h3\u003E\n\u003Cp\u003ECustomize behavior and integrate with your system:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EServer Options\u003C/li\u003E\n\u003Cli\u003EClient Options\u003C/li\u003E\n\u003Cli\u003EConfiguration Sources\u003C/li\u003E\n\u003Cli\u003EAdvanced Configuration\u003C/li\u003E\n\u003Cli\u003EExtending Domains \u0026amp; Stores\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022client-api\u0022\u003E6) Client \u0026amp; API\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EReal world usage of UltimateAuth\u003C/li\u003E\n\u003Cli\u003EAPI and code examples of authentication, sessions, users, credentials and authorization\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-advanced\u0022\u003E7) Security \u0026amp; Advanced\u003C/h3\u003E\n\u003Cp\u003EDeep dive into the security model:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ESession Security Model\u003C/li\u003E\n\u003Cli\u003ERefresh Token Rotation\u003C/li\u003E\n\u003Cli\u003EAccess Token Behavior\u003C/li\u003E\n\u003Cli\u003EPolicy Pipeline\u003C/li\u003E\n\u003C/ul\u003E\n", + "Headings": [ + { + "Id": "sections", + "Text": "Sections", + "Level": 0 + }, + { + "Id": "getting-started", + "Text": "1) Getting Started", + "Level": 1 + }, + { + "Id": "fundamentals", + "Text": "2) Fundamentals", + "Level": 1 + }, + { + "Id": "auth-flows", + "Text": "3) Auth Flows", + "Level": 1 + }, + { + "Id": "plugin-domains", + "Text": "4) Plugin Domains", + "Level": 1 + }, + { + "Id": "configuration-extensibility", + "Text": "5) Configuration \u0026 Extensibility", + "Level": 1 + }, + { + "Id": "client-api", + "Text": "6) Client \u0026 API", + "Level": 1 + }, + { + "Id": "security-advanced", + "Text": "7) Security \u0026 Advanced", + "Level": 1 + } + ] } \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/access-token-behavior.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/access-token-behavior.json index 50f2f81e..d90f47f3 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/access-token-behavior.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/access-token-behavior.json @@ -1,5 +1,97 @@ { "Slug": "security/access-token-behavior", "Title": "Access Token Behavior", - "Html": "\n\u003Cp\u003EAccess tokens in UltimateAuth are intentionally \u003Cstrong\u003Eshort-lived and mode-aware\u003C/strong\u003E.\nThey are not the primary source of truth for authentication.\u003C/p\u003E\n\u003Ch2 id=\u0022core-principle\u0022\u003E\uD83E\uDDE0 Core Principle\u003C/h2\u003E\n\u003Cp\u003EIn UltimateAuth:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Access tokens are \u003Cstrong\u003Etransport tokens\u003C/strong\u003E\n\uD83D\uDC49 Sessions are the \u003Cstrong\u003Esource of truth\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022token-types\u0022\u003E\uD83D\uDD11 Token Types\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth supports two access token types:\u003C/p\u003E\n\u003Ch3 id=\u0022opaque-tokens\u0022\u003E\uD83D\uDD39 Opaque Tokens\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ERandom, non-readable values\u003C/li\u003E\n\u003Cli\u003EStored and validated server-side\u003C/li\u003E\n\u003Cli\u003ETypically used with session-based auth\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022jwt-tokens\u0022\u003E\uD83D\uDD39 JWT Tokens\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ESelf-contained tokens\u003C/li\u003E\n\u003Cli\u003EContain claims (user, tenant, session, etc.)\u003C/li\u003E\n\u003Cli\u003ESigned and verifiable without storage\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022mode-dependent-behavior\u0022\u003E\u2699\uFE0F Mode-Dependent Behavior\u003C/h2\u003E\n\u003Cp\u003EAccess token behavior depends on auth mode:\u003C/p\u003E\n\u003Ch3 id=\u0022pureopaque\u0022\u003E\uD83D\uDFE2 PureOpaque\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ENo JWT issued\u003C/li\u003E\n\u003Cli\u003ESession cookie is the primary mechanism\u003C/li\u003E\n\u003Cli\u003EAccess token may not exist externally\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Validation = session validation\u003C/p\u003E\n\u003Ch3 id=\u0022hybrid\u0022\u003E\uD83D\uDFE1 Hybrid\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EOpaque \u002B JWT together\u003C/li\u003E\n\u003Cli\u003ESession still authoritative\u003C/li\u003E\n\u003Cli\u003EJWT used for API access\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Validation = session \u002B token\u003C/p\u003E\n\u003Ch3 id=\u0022semihybrid\u0022\u003E\uD83D\uDFE0 SemiHybrid\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EJWT is primary access token\u003C/li\u003E\n\u003Cli\u003ESession still exists\u003C/li\u003E\n\u003Cli\u003ERefresh rotation enabled\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Balanced approach\u003C/p\u003E\n\u003Ch3 id=\u0022purejwt\u0022\u003E\uD83D\uDD35 PureJwt\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EOnly JWT \u002B refresh tokens\u003C/li\u003E\n\u003Cli\u003ENo session state required\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Stateless mode\u003C/p\u003E\n\u003Ch2 id=\u0022lifetime-strategy\u0022\u003E\u23F1 Lifetime Strategy\u003C/h2\u003E\n\u003Cp\u003EAccess tokens are:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eshort-lived\u003C/li\u003E\n\u003Cli\u003Ereplaceable\u003C/li\u003E\n\u003Cli\u003Enot trusted long-term\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003ETypical lifetime:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003E5\u201315 minutes\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022refresh-interaction\u0022\u003E\uD83D\uDD04 Refresh Interaction\u003C/h2\u003E\n\u003Cp\u003EAccess tokens are never extended directly.\u003C/p\u003E\n\u003Cp\u003EInstead:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EAccess Token \u2192 expires \u2192 Refresh \u2192 new Access Token\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This ensures:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eforward-only security\u003C/li\u003E\n\u003Cli\u003Eno silent extension\u003C/li\u003E\n\u003Cli\u003Ereplay window minimization\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022claims-model\u0022\u003E\uD83E\uDDFE Claims Model\u003C/h2\u003E\n\u003Cp\u003EJWT tokens may include:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Esub (user id)\u003C/li\u003E\n\u003Cli\u003Etenant\u003C/li\u003E\n\u003Cli\u003Esid (session id)\u003C/li\u003E\n\u003Cli\u003Ejti (token id)\u003C/li\u003E\n\u003Cli\u003Ecustom claims\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Claims are generated at issuance time\n\uD83D\uDC49 Not dynamically updated afterward\u003C/p\u003E\n\u003Ch2 id=\u0022important-implication\u0022\u003E\u26A0\uFE0F Important Implication\u003C/h2\u003E\n\u003Cp\u003EIf something changes:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Euser roles\u003C/li\u003E\n\u003Cli\u003Epermissions\u003C/li\u003E\n\u003Cli\u003Esecurity state\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 existing JWTs are NOT updated\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 This is why:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Etokens are short-lived\u003C/li\u003E\n\u003Cli\u003Erefresh is required\u003C/li\u003E\n\u003Cli\u003Esession validation may still apply\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022security-boundaries\u0022\u003E\uD83D\uDEE1 Security Boundaries\u003C/h2\u003E\n\u003Cp\u003EAccess tokens are:\u003C/p\u003E\n\u003Cp\u003E\u274C not revocable individually (JWT)\n\u274C not long-term identity\u003C/p\u003E\n\u003Cp\u003E\u2714 tied to session or refresh flow\n\u2714 bounded by expiration\u003C/p\u003E\n\u003Ch2 id=\u0022why-this-matters\u0022\u003E\uD83D\uDD25 Why This Matters\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth avoids a common mistake:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 treating JWT as the system of record\u003C/p\u003E\n\u003Cp\u003EInstead:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 JWT is a \u003Cstrong\u003Esnapshot\u003C/strong\u003E, not truth\u003C/p\u003E\n\u003Ch2 id=\u0022design-tradeoff\u0022\u003E\u26A0\uFE0F Design Tradeoff\u003C/h2\u003E\n\u003Cp\u003EShort-lived tokens mean:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Emore refresh calls\u003C/li\u003E\n\u003Cli\u003Eslightly more backend interaction\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 But this enables:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Esafer rotation\u003C/li\u003E\n\u003Cli\u003Ebetter revocation\u003C/li\u003E\n\u003Cli\u003Ereduced attack window\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Access tokens are \u003Cstrong\u003Etemporary access grants\u003C/strong\u003E\n\uD83D\uDC49 Not persistent identity\u003C/p\u003E\n\u003Ch2 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003EPolicy Pipeline Deep Dive\u003C/strong\u003E to understand how access decisions are enforced.\u003C/p\u003E\n" + "Html": "\n\u003Cp\u003EAccess tokens in UltimateAuth are intentionally \u003Cstrong\u003Eshort-lived and mode-aware\u003C/strong\u003E.\nThey are not the primary source of truth for authentication.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022core-principle\u0022\u003E\uD83E\uDDE0 Core Principle\u003C/h2\u003E\n\u003Cp\u003EIn UltimateAuth:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Access tokens are \u003Cstrong\u003Etransport tokens\u003C/strong\u003E\n\uD83D\uDC49 Sessions are the \u003Cstrong\u003Esource of truth\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022token-types\u0022\u003E\uD83D\uDD11 Token Types\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth supports two access token types:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022opaque-tokens\u0022\u003E\uD83D\uDD39 Opaque Tokens\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ERandom, non-readable values\u003C/li\u003E\n\u003Cli\u003EStored and validated server-side\u003C/li\u003E\n\u003Cli\u003ETypically used with session-based auth\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022jwt-tokens\u0022\u003E\uD83D\uDD39 JWT Tokens\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ESelf-contained tokens\u003C/li\u003E\n\u003Cli\u003EContain claims (user, tenant, session, etc.)\u003C/li\u003E\n\u003Cli\u003ESigned and verifiable without storage\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mode-dependent-behavior\u0022\u003E\u2699\uFE0F Mode-Dependent Behavior\u003C/h2\u003E\n\u003Cp\u003EAccess token behavior depends on auth mode:\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022pureopaque\u0022\u003E\uD83D\uDFE2 PureOpaque\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003ENo JWT issued\u003C/li\u003E\n\u003Cli\u003ESession cookie is the primary mechanism\u003C/li\u003E\n\u003Cli\u003EAccess token may not exist externally\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Validation = session validation\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022hybrid\u0022\u003E\uD83D\uDFE1 Hybrid\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EOpaque \u002B JWT together\u003C/li\u003E\n\u003Cli\u003ESession still authoritative\u003C/li\u003E\n\u003Cli\u003EJWT used for API access\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Validation = session \u002B token\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022semihybrid\u0022\u003E\uD83D\uDFE0 SemiHybrid\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EJWT is primary access token\u003C/li\u003E\n\u003Cli\u003ESession still exists\u003C/li\u003E\n\u003Cli\u003ERefresh rotation enabled\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Balanced approach\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022purejwt\u0022\u003E\uD83D\uDD35 PureJwt\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EOnly JWT \u002B refresh tokens\u003C/li\u003E\n\u003Cli\u003ENo session state required\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Stateless mode\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022lifetime-strategy\u0022\u003E\u23F1 Lifetime Strategy\u003C/h2\u003E\n\u003Cp\u003EAccess tokens are:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eshort-lived\u003C/li\u003E\n\u003Cli\u003Ereplaceable\u003C/li\u003E\n\u003Cli\u003Enot trusted long-term\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003ETypical lifetime:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003E5\u201315 minutes\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022refresh-interaction\u0022\u003E\uD83D\uDD04 Refresh Interaction\u003C/h2\u003E\n\u003Cp\u003EAccess tokens are never extended directly.\u003C/p\u003E\n\u003Cp\u003EInstead:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EAccess Token \u2192 expires \u2192 Refresh \u2192 new Access Token\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This ensures:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eforward-only security\u003C/li\u003E\n\u003Cli\u003Eno silent extension\u003C/li\u003E\n\u003Cli\u003Ereplay window minimization\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022claims-model\u0022\u003E\uD83E\uDDFE Claims Model\u003C/h2\u003E\n\u003Cp\u003EJWT tokens may include:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Esub (user id)\u003C/li\u003E\n\u003Cli\u003Etenant\u003C/li\u003E\n\u003Cli\u003Esid (session id)\u003C/li\u003E\n\u003Cli\u003Ejti (token id)\u003C/li\u003E\n\u003Cli\u003Ecustom claims\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Claims are generated at issuance time\n\uD83D\uDC49 Not dynamically updated afterward\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022important-implication\u0022\u003E\u26A0\uFE0F Important Implication\u003C/h2\u003E\n\u003Cp\u003EIf something changes:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Euser roles\u003C/li\u003E\n\u003Cli\u003Epermissions\u003C/li\u003E\n\u003Cli\u003Esecurity state\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 existing JWTs are NOT updated\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 This is why:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Etokens are short-lived\u003C/li\u003E\n\u003Cli\u003Erefresh is required\u003C/li\u003E\n\u003Cli\u003Esession validation may still apply\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-boundaries\u0022\u003E\uD83D\uDEE1 Security Boundaries\u003C/h2\u003E\n\u003Cp\u003EAccess tokens are:\u003C/p\u003E\n\u003Cp\u003E\u274C not revocable individually (JWT)\n\u274C not long-term identity\u003C/p\u003E\n\u003Cp\u003E\u2714 tied to session or refresh flow\n\u2714 bounded by expiration\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022why-this-matters\u0022\u003E\uD83D\uDD25 Why This Matters\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth avoids a common mistake:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 treating JWT as the system of record\u003C/p\u003E\n\u003Cp\u003EInstead:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 JWT is a \u003Cstrong\u003Esnapshot\u003C/strong\u003E, not truth\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022design-tradeoff\u0022\u003E\u26A0\uFE0F Design Tradeoff\u003C/h2\u003E\n\u003Cp\u003EShort-lived tokens mean:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Emore refresh calls\u003C/li\u003E\n\u003Cli\u003Eslightly more backend interaction\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 But this enables:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Esafer rotation\u003C/li\u003E\n\u003Cli\u003Ebetter revocation\u003C/li\u003E\n\u003Cli\u003Ereduced attack window\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Access tokens are \u003Cstrong\u003Etemporary access grants\u003C/strong\u003E\n\uD83D\uDC49 Not persistent identity\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003EPolicy Pipeline Deep Dive\u003C/strong\u003E to understand how access decisions are enforced.\u003C/p\u003E\n", + "Headings": [ + { + "Id": "core-principle", + "Text": "\uD83E\uDDE0 Core Principle", + "Level": 0 + }, + { + "Id": "token-types", + "Text": "\uD83D\uDD11 Token Types", + "Level": 0 + }, + { + "Id": "opaque-tokens", + "Text": "\uD83D\uDD39 Opaque Tokens", + "Level": 1 + }, + { + "Id": "jwt-tokens", + "Text": "\uD83D\uDD39 JWT Tokens", + "Level": 1 + }, + { + "Id": "mode-dependent-behavior", + "Text": "\u2699\uFE0F Mode-Dependent Behavior", + "Level": 0 + }, + { + "Id": "pureopaque", + "Text": "\uD83D\uDFE2 PureOpaque", + "Level": 1 + }, + { + "Id": "hybrid", + "Text": "\uD83D\uDFE1 Hybrid", + "Level": 1 + }, + { + "Id": "semihybrid", + "Text": "\uD83D\uDFE0 SemiHybrid", + "Level": 1 + }, + { + "Id": "purejwt", + "Text": "\uD83D\uDD35 PureJwt", + "Level": 1 + }, + { + "Id": "lifetime-strategy", + "Text": "\u23F1 Lifetime Strategy", + "Level": 0 + }, + { + "Id": "refresh-interaction", + "Text": "\uD83D\uDD04 Refresh Interaction", + "Level": 0 + }, + { + "Id": "claims-model", + "Text": "\uD83E\uDDFE Claims Model", + "Level": 0 + }, + { + "Id": "important-implication", + "Text": "\u26A0\uFE0F Important Implication", + "Level": 0 + }, + { + "Id": "security-boundaries", + "Text": "\uD83D\uDEE1 Security Boundaries", + "Level": 0 + }, + { + "Id": "why-this-matters", + "Text": "\uD83D\uDD25 Why This Matters", + "Level": 0 + }, + { + "Id": "design-tradeoff", + "Text": "\u26A0\uFE0F Design Tradeoff", + "Level": 0 + }, + { + "Id": "mental-model", + "Text": "\uD83E\uDDE0 Mental Model", + "Level": 0 + }, + { + "Id": "next-step", + "Text": "\u27A1\uFE0F Next Step", + "Level": 0 + } + ] } \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/policy-pipeline.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/policy-pipeline.json index 48e2d245..85cf991d 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/policy-pipeline.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/policy-pipeline.json @@ -1,5 +1,77 @@ { "Slug": "security/policy-pipeline", "Title": "Policy Pipeline", - "Html": "\n\u003Cp\u003EUltimateAuth does not rely on simple role checks.\u003C/p\u003E\n\u003Cp\u003EAuthorization is executed through a \u003Cstrong\u003Emulti-stage policy pipeline\u003C/strong\u003E that evaluates:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Einvariants (always enforced rules)\u003C/li\u003E\n\u003Cli\u003Eglobal policies\u003C/li\u003E\n\u003Cli\u003Eruntime (action-based) policies\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022core-principle\u0022\u003E\uD83E\uDDE0 Core Principle\u003C/h2\u003E\n\u003Cp\u003E\uD83D\uDC49 Authorization is not a single check\u003Cbr /\u003E\n\uD83D\uDC49 It is a \u003Cstrong\u003Edecision pipeline\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022high-level-flow\u0022\u003E\uD83D\uDD01 High-Level Flow\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EAccessContext\n \u2193\nEnrichment (claims, permissions)\n \u2193\nInvariants\n \u2193\nGlobal Policies\n \u2193\nRuntime Policies (action-based)\n \u2193\nFinal Decision\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022accesscontext\u0022\u003E\uD83E\uDDE9 AccessContext\u003C/h2\u003E\n\u003Cp\u003EEvery authorization decision is based on an \u003Ccode\u003EAccessContext\u003C/code\u003E.\u003C/p\u003E\n\u003Cp\u003EIt contains:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eactor (who is making the request)\u003C/li\u003E\n\u003Cli\u003Etarget (resource / user)\u003C/li\u003E\n\u003Cli\u003Etenant\u003C/li\u003E\n\u003Cli\u003Eaction (string-based, e.g. \u0026quot;users.delete\u0026quot;)\u003C/li\u003E\n\u003Cli\u003Eattributes (claims, permissions, etc.)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Everything in the pipeline reads from this context\u003C/p\u003E\n\u003Ch2 id=\u0022step-1-context-enrichment\u0022\u003E\uD83D\uDD04 Step 1: Context Enrichment\u003C/h2\u003E\n\u003Cp\u003EBefore policies run, context is enriched:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Epermissions are loaded\u003C/li\u003E\n\u003Cli\u003Ecompiled into \u003Ccode\u003ECompiledPermissionSet\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003Eattached to context\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This allows policies to run without hitting storage repeatedly\u003C/p\u003E\n\u003Ch2 id=\u0022step-2-invariants\u0022\u003E\uD83D\uDEE1 Step 2: Invariants\u003C/h2\u003E\n\u003Cp\u003EInvariants are \u003Cstrong\u003Ealways enforced rules\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EThey run first and cannot be bypassed.\u003C/p\u003E\n\u003Cp\u003EExamples:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Euser must be authenticated\u003C/li\u003E\n\u003Cli\u003Ecross-tenant access is denied\u003C/li\u003E\n\u003Cli\u003Erequest must be valid\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 If an invariant fails:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003E\u2192 DENY immediately\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022step-3-global-policies\u0022\u003E\uD83C\uDF10 Step 3: Global Policies\u003C/h2\u003E\n\u003Cp\u003EGlobal policies apply to all requests but are conditional.\u003C/p\u003E\n\u003Cp\u003EExamples:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERequireActiveUserPolicy\u003C/li\u003E\n\u003Cli\u003EDenyAdminSelfModificationPolicy\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EEach policy:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Edecides if it applies (\u003Ccode\u003EAppliesTo\u003C/code\u003E)\u003C/li\u003E\n\u003Cli\u003Eevaluates (\u003Ccode\u003EDecide\u003C/code\u003E)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Important:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u201CAllow\u201D means \u003Cem\u003Eno objection\u003C/em\u003E\u003C/li\u003E\n\u003Cli\u003Enot final approval\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022step-4-runtime-policies\u0022\u003E\uD83C\uDFAF Step 4: Runtime Policies\u003C/h2\u003E\n\u003Cp\u003ERuntime policies are:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eaction-based\u003C/li\u003E\n\u003Cli\u003Edynamically resolved\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThey come from:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EAccessPolicyRegistry \u2192 CompiledAccessPolicySet\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EPolicies are selected by:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003Econtext.Action.StartsWith(prefix)\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EExample:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EAction: users.delete.admin\n\nMatches:\n- users.*\n- users.delete.*\n- users.delete.admin\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022step-5-decision-engine\u0022\u003E\u2696\uFE0F Step 5: Decision Engine\u003C/h2\u003E\n\u003Cp\u003EAll policies are evaluated by:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ccode\u003EIAccessAuthority\u003C/code\u003E\u003C/p\u003E\n\u003Cp\u003EEvaluation order:\u003C/p\u003E\n\u003Col\u003E\n\u003Cli\u003EInvariants\u003C/li\u003E\n\u003Cli\u003EGlobal policies\u003C/li\u003E\n\u003Cli\u003ERuntime policies\u003C/li\u003E\n\u003C/ol\u003E\n\u003Ch3 id=\u0022decision-rules\u0022\u003EDecision Rules\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EFirst \u003Cstrong\u003Edeny\u003C/strong\u003E \u2192 stops execution\u003C/li\u003E\n\u003Cli\u003E\u201CAllow\u201D \u2192 continue\u003C/li\u003E\n\u003Cli\u003E\u201CRequiresReauthentication\u201D \u2192 tracked\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EFinal result:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EAllow\nDeny(reason)\nReauthenticationRequired\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022permission-integration\u0022\u003E\uD83D\uDD10 Permission Integration\u003C/h2\u003E\n\u003Cp\u003EPermissions are not directly checked in services.\u003C/p\u003E\n\u003Cp\u003EInstead:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Epermissions are compiled\u003C/li\u003E\n\u003Cli\u003Epolicies use them\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EExample policy:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EMustHavePermissionPolicy\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EChecks:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ECompiledPermissionSet.IsAllowed(action)\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This decouples:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Epermission storage\u003C/li\u003E\n\u003Cli\u003Eauthorization logic\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022why-this-matters\u0022\u003E\uD83D\uDD25 Why This Matters\u003C/h2\u003E\n\u003Cp\u003EThis pipeline enables:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Ecomposable security rules\u003C/li\u003E\n\u003Cli\u003Econsistent enforcement\u003C/li\u003E\n\u003Cli\u003Eseparation of concerns\u003C/li\u003E\n\u003Cli\u003Eextensibility\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003ECompared to typical systems:\u003C/p\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EFeature\u003C/th\u003E\n\u003Cth\u003ETraditional\u003C/th\u003E\n\u003Cth\u003EUltimateAuth\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EInline checks\u003C/td\u003E\n\u003Ctd\u003E\u2705\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ECentral pipeline\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003E\u2705\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EPolicy composition\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003E\u2705\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EAction-based rules\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003E\u2705\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Ch2 id=\u0022design-tradeoff\u0022\u003E\u26A0\uFE0F Design Tradeoff\u003C/h2\u003E\n\u003Cp\u003EThis model introduces:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Emore abstraction\u003C/li\u003E\n\u003Cli\u003Emore components\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 But gives:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Epredictability\u003C/li\u003E\n\u003Cli\u003Eauditability\u003C/li\u003E\n\u003Cli\u003Eflexibility\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Authorization is not \u201Cif (role == admin)\u201D\u003Cbr /\u003E\n\uD83D\uDC49 It is a \u003Cstrong\u003Epipeline of decisions\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022summary\u0022\u003E\u27A1\uFE0F Summary\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth authorization:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eis policy-driven\u003C/li\u003E\n\u003Cli\u003Eruns through a structured pipeline\u003C/li\u003E\n\u003Cli\u003Eseparates invariants, global rules, and action rules\u003C/li\u003E\n\u003Cli\u003Eproduces deterministic decisions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This makes it suitable for complex, multi-tenant, security-sensitive systems\u003C/p\u003E\n" + "Html": "\n\u003Cp\u003EUltimateAuth does not rely on simple role checks.\u003C/p\u003E\n\u003Cp\u003EAuthorization is executed through a \u003Cstrong\u003Emulti-stage policy pipeline\u003C/strong\u003E that evaluates:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Einvariants (always enforced rules)\u003C/li\u003E\n\u003Cli\u003Eglobal policies\u003C/li\u003E\n\u003Cli\u003Eruntime (action-based) policies\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022core-principle\u0022\u003E\uD83E\uDDE0 Core Principle\u003C/h2\u003E\n\u003Cp\u003E\uD83D\uDC49 Authorization is not a single check\u003Cbr /\u003E\n\uD83D\uDC49 It is a \u003Cstrong\u003Edecision pipeline\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022high-level-flow\u0022\u003E\uD83D\uDD01 High-Level Flow\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EAccessContext\n \u2193\nEnrichment (claims, permissions)\n \u2193\nInvariants\n \u2193\nGlobal Policies\n \u2193\nRuntime Policies (action-based)\n \u2193\nFinal Decision\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022accesscontext\u0022\u003E\uD83E\uDDE9 AccessContext\u003C/h2\u003E\n\u003Cp\u003EEvery authorization decision is based on an \u003Ccode\u003EAccessContext\u003C/code\u003E.\u003C/p\u003E\n\u003Cp\u003EIt contains:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eactor (who is making the request)\u003C/li\u003E\n\u003Cli\u003Etarget (resource / user)\u003C/li\u003E\n\u003Cli\u003Etenant\u003C/li\u003E\n\u003Cli\u003Eaction (string-based, e.g. \u0026quot;users.delete\u0026quot;)\u003C/li\u003E\n\u003Cli\u003Eattributes (claims, permissions, etc.)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Everything in the pipeline reads from this context\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022step-1-context-enrichment\u0022\u003E\uD83D\uDD04 Step 1: Context Enrichment\u003C/h2\u003E\n\u003Cp\u003EBefore policies run, context is enriched:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Epermissions are loaded\u003C/li\u003E\n\u003Cli\u003Ecompiled into \u003Ccode\u003ECompiledPermissionSet\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003Eattached to context\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This allows policies to run without hitting storage repeatedly\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022step-2-invariants\u0022\u003E\uD83D\uDEE1 Step 2: Invariants\u003C/h2\u003E\n\u003Cp\u003EInvariants are \u003Cstrong\u003Ealways enforced rules\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EThey run first and cannot be bypassed.\u003C/p\u003E\n\u003Cp\u003EExamples:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Euser must be authenticated\u003C/li\u003E\n\u003Cli\u003Ecross-tenant access is denied\u003C/li\u003E\n\u003Cli\u003Erequest must be valid\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 If an invariant fails:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003E\u2192 DENY immediately\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022step-3-global-policies\u0022\u003E\uD83C\uDF10 Step 3: Global Policies\u003C/h2\u003E\n\u003Cp\u003EGlobal policies apply to all requests but are conditional.\u003C/p\u003E\n\u003Cp\u003EExamples:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ERequireActiveUserPolicy\u003C/li\u003E\n\u003Cli\u003EDenyAdminSelfModificationPolicy\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EEach policy:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Edecides if it applies (\u003Ccode\u003EAppliesTo\u003C/code\u003E)\u003C/li\u003E\n\u003Cli\u003Eevaluates (\u003Ccode\u003EDecide\u003C/code\u003E)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Important:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003E\u201CAllow\u201D means \u003Cem\u003Eno objection\u003C/em\u003E\u003C/li\u003E\n\u003Cli\u003Enot final approval\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022step-4-runtime-policies\u0022\u003E\uD83C\uDFAF Step 4: Runtime Policies\u003C/h2\u003E\n\u003Cp\u003ERuntime policies are:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eaction-based\u003C/li\u003E\n\u003Cli\u003Edynamically resolved\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThey come from:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EAccessPolicyRegistry \u2192 CompiledAccessPolicySet\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EPolicies are selected by:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003Econtext.Action.StartsWith(prefix)\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EExample:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EAction: users.delete.admin\n\nMatches:\n- users.*\n- users.delete.*\n- users.delete.admin\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022step-5-decision-engine\u0022\u003E\u2696\uFE0F Step 5: Decision Engine\u003C/h2\u003E\n\u003Cp\u003EAll policies are evaluated by:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Ccode\u003EIAccessAuthority\u003C/code\u003E\u003C/p\u003E\n\u003Cp\u003EEvaluation order:\u003C/p\u003E\n\u003Col\u003E\n\u003Cli\u003EInvariants\u003C/li\u003E\n\u003Cli\u003EGlobal policies\u003C/li\u003E\n\u003Cli\u003ERuntime policies\u003C/li\u003E\n\u003C/ol\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022decision-rules\u0022\u003EDecision Rules\u003C/h3\u003E\n\u003Cul\u003E\n\u003Cli\u003EFirst \u003Cstrong\u003Edeny\u003C/strong\u003E \u2192 stops execution\u003C/li\u003E\n\u003Cli\u003E\u201CAllow\u201D \u2192 continue\u003C/li\u003E\n\u003Cli\u003E\u201CRequiresReauthentication\u201D \u2192 tracked\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EFinal result:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EAllow\nDeny(reason)\nReauthenticationRequired\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022permission-integration\u0022\u003E\uD83D\uDD10 Permission Integration\u003C/h2\u003E\n\u003Cp\u003EPermissions are not directly checked in services.\u003C/p\u003E\n\u003Cp\u003EInstead:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Epermissions are compiled\u003C/li\u003E\n\u003Cli\u003Epolicies use them\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EExample policy:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EMustHavePermissionPolicy\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EChecks:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ECompiledPermissionSet.IsAllowed(action)\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 This decouples:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Epermission storage\u003C/li\u003E\n\u003Cli\u003Eauthorization logic\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022why-this-matters\u0022\u003E\uD83D\uDD25 Why This Matters\u003C/h2\u003E\n\u003Cp\u003EThis pipeline enables:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Ecomposable security rules\u003C/li\u003E\n\u003Cli\u003Econsistent enforcement\u003C/li\u003E\n\u003Cli\u003Eseparation of concerns\u003C/li\u003E\n\u003Cli\u003Eextensibility\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003ECompared to typical systems:\u003C/p\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EFeature\u003C/th\u003E\n\u003Cth\u003ETraditional\u003C/th\u003E\n\u003Cth\u003EUltimateAuth\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EInline checks\u003C/td\u003E\n\u003Ctd\u003E\u2705\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003ECentral pipeline\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003E\u2705\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EPolicy composition\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003E\u2705\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EAction-based rules\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003E\u2705\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022design-tradeoff\u0022\u003E\u26A0\uFE0F Design Tradeoff\u003C/h2\u003E\n\u003Cp\u003EThis model introduces:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Emore abstraction\u003C/li\u003E\n\u003Cli\u003Emore components\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 But gives:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Epredictability\u003C/li\u003E\n\u003Cli\u003Eauditability\u003C/li\u003E\n\u003Cli\u003Eflexibility\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Authorization is not \u201Cif (role == admin)\u201D\u003Cbr /\u003E\n\uD83D\uDC49 It is a \u003Cstrong\u003Epipeline of decisions\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022summary\u0022\u003E\u27A1\uFE0F Summary\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth authorization:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eis policy-driven\u003C/li\u003E\n\u003Cli\u003Eruns through a structured pipeline\u003C/li\u003E\n\u003Cli\u003Eseparates invariants, global rules, and action rules\u003C/li\u003E\n\u003Cli\u003Eproduces deterministic decisions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This makes it suitable for complex, multi-tenant, security-sensitive systems\u003C/p\u003E\n", + "Headings": [ + { + "Id": "core-principle", + "Text": "\uD83E\uDDE0 Core Principle", + "Level": 0 + }, + { + "Id": "high-level-flow", + "Text": "\uD83D\uDD01 High-Level Flow", + "Level": 0 + }, + { + "Id": "accesscontext", + "Text": "\uD83E\uDDE9 AccessContext", + "Level": 0 + }, + { + "Id": "step-1-context-enrichment", + "Text": "\uD83D\uDD04 Step 1: Context Enrichment", + "Level": 0 + }, + { + "Id": "step-2-invariants", + "Text": "\uD83D\uDEE1 Step 2: Invariants", + "Level": 0 + }, + { + "Id": "step-3-global-policies", + "Text": "\uD83C\uDF10 Step 3: Global Policies", + "Level": 0 + }, + { + "Id": "step-4-runtime-policies", + "Text": "\uD83C\uDFAF Step 4: Runtime Policies", + "Level": 0 + }, + { + "Id": "step-5-decision-engine", + "Text": "\u2696\uFE0F Step 5: Decision Engine", + "Level": 0 + }, + { + "Id": "decision-rules", + "Text": "Decision Rules", + "Level": 1 + }, + { + "Id": "permission-integration", + "Text": "\uD83D\uDD10 Permission Integration", + "Level": 0 + }, + { + "Id": "why-this-matters", + "Text": "\uD83D\uDD25 Why This Matters", + "Level": 0 + }, + { + "Id": "design-tradeoff", + "Text": "\u26A0\uFE0F Design Tradeoff", + "Level": 0 + }, + { + "Id": "mental-model", + "Text": "\uD83E\uDDE0 Mental Model", + "Level": 0 + }, + { + "Id": "summary", + "Text": "\u27A1\uFE0F Summary", + "Level": 0 + } + ] } \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/refresh-rotation.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/refresh-rotation.json index cc33583a..dd21f370 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/refresh-rotation.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/refresh-rotation.json @@ -1,5 +1,87 @@ { "Slug": "security/refresh-rotation", "Title": "Refresh Rotation", - "Html": "\n\u003Cp\u003ERefresh tokens in UltimateAuth are not simple long-lived tokens.\u003C/p\u003E\n\u003Cp\u003EThey are part of a \u003Cstrong\u003Erotation-based security system\u003C/strong\u003E designed to:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eprevent token replay\u003C/li\u003E\n\u003Cli\u003Edetect token theft\u003C/li\u003E\n\u003Cli\u003Eenforce forward-only session progression\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022why-rotation\u0022\u003E\uD83E\uDDE0 Why Rotation?\u003C/h2\u003E\n\u003Cp\u003EIn traditional systems:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Erefresh tokens are long-lived\u003C/li\u003E\n\u003Cli\u003Ethey can be reused multiple times\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 If stolen, an attacker can:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Esilently keep refreshing access tokens\u003C/li\u003E\n\u003Cli\u003Estay undetected\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EUltimateAuth solves this with:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Cstrong\u003Esingle-use refresh tokens (rotation)\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022rotation-model\u0022\u003E\uD83D\uDD01 Rotation Model\u003C/h2\u003E\n\u003Cp\u003EEach refresh token is:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eused exactly once\u003C/li\u003E\n\u003Cli\u003Ereplaced with a new token\u003C/li\u003E\n\u003Cli\u003Elinked to a chain\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EToken A \u2192 Token B \u2192 Token C \u2192 ...\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EWhen a refresh happens:\u003C/p\u003E\n\u003Col\u003E\n\u003Cli\u003EToken A is validated\u003C/li\u003E\n\u003Cli\u003EToken A is \u003Cstrong\u003Erevoked\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EToken B is issued\u003C/li\u003E\n\u003Cli\u003EToken A is marked as replaced by B\u003C/li\u003E\n\u003C/ol\u003E\n\u003Ch2 id=\u0022token-state\u0022\u003E\uD83D\uDD10 Token State\u003C/h2\u003E\n\u003Cp\u003EA refresh token can be:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EActive \u2192 valid and usable\u003C/li\u003E\n\u003Cli\u003ERevoked \u2192 already used or manually revoked\u003C/li\u003E\n\u003Cli\u003EExpired \u2192 lifetime exceeded\u003C/li\u003E\n\u003Cli\u003EReplaced \u2192 already rotated\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Only \u003Cstrong\u003Eactive tokens\u003C/strong\u003E are valid\u003C/p\u003E\n\u003Ch2 id=\u0022reuse-detection\u0022\u003E\uD83D\uDEA8 Reuse Detection\u003C/h2\u003E\n\u003Cp\u003EThis is the most critical security feature.\u003C/p\u003E\n\u003Cp\u003EIf a refresh token is used \u003Cstrong\u003Eafter it has already been rotated\u003C/strong\u003E, it means:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 The token was reused\u003Cbr /\u003E\n\uD83D\uDC49 Likely stolen\u003C/p\u003E\n\u003Ch3 id=\u0022what-happens\u0022\u003EWhat happens?\u003C/h3\u003E\n\u003Cp\u003EWhen reuse is detected:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Ethe system identifies the session chain\u003C/li\u003E\n\u003Cli\u003Ethe entire chain can be revoked\u003C/li\u003E\n\u003Cli\u003Eall related sessions become invalid\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This immediately cuts off both:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eattacker\u003C/li\u003E\n\u003Cli\u003Elegitimate user (forcing reauthentication)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022chain-awareness\u0022\u003E\uD83D\uDD17 Chain Awareness\u003C/h2\u003E\n\u003Cp\u003ERefresh tokens belong to a \u003Cstrong\u003Esession chain\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EThis enables:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Etracking rotation history\u003C/li\u003E\n\u003Cli\u003Edetecting anomalies\u003C/li\u003E\n\u003Cli\u003Eapplying revocation at the correct scope\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EWithout chains:\u003C/p\u003E\n\u003Cp\u003E\u274C You cannot safely detect reuse\u003Cbr /\u003E\n\u274C You cannot know which tokens belong together\u003C/p\u003E\n\u003Ch2 id=\u0022rotation-flow\u0022\u003E\uD83D\uDD04 Rotation Flow\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EClient \u2192 Refresh(Token A)\n \u2192 Validate A\n \u2192 Revoke A\n \u2192 Issue B\n \u2192 Return new tokens\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022invalid-scenarios\u0022\u003E\u26A0\uFE0F Invalid Scenarios\u003C/h2\u003E\n\u003Ch3 id=\u0022expired-token\u0022\u003E1. Expired Token\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EToken expired \u2192 reject\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022revoked-token\u0022\u003E2. Revoked Token\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EToken already used \u2192 reuse detected\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 id=\u0022session-mismatch\u0022\u003E3. Session Mismatch\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EToken does not belong to expected session \u2192 reject\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 id=\u0022security-guarantees\u0022\u003E\uD83E\uDDE0 Security Guarantees\u003C/h2\u003E\n\u003Cp\u003ERotation ensures:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Erefresh tokens are forward-only\u003C/li\u003E\n\u003Cli\u003Eold tokens cannot be reused safely\u003C/li\u003E\n\u003Cli\u003Estolen tokens are detectable\u003C/li\u003E\n\u003Cli\u003Ecompromise triggers containment\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022why-this-matters\u0022\u003E\uD83D\uDD25 Why This Matters\u003C/h2\u003E\n\u003Cp\u003ECompared to traditional refresh tokens:\u003C/p\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EFeature\u003C/th\u003E\n\u003Cth\u003ETraditional\u003C/th\u003E\n\u003Cth\u003EUltimateAuth\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EReusable tokens\u003C/td\u003E\n\u003Ctd\u003E\u2705\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EReuse detection\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003E\u2705\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EChain awareness\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003E\u2705\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EAutomatic containment\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003E\u2705\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Ch2 id=\u0022design-tradeoff\u0022\u003E\u26A0\uFE0F Design Tradeoff\u003C/h2\u003E\n\u003Cp\u003ERotation requires:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Etoken storage\u003C/li\u003E\n\u003Cli\u003Estate tracking\u003C/li\u003E\n\u003Cli\u003Eadditional validation logic\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 UltimateAuth chooses security over simplicity.\u003C/p\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 A refresh token is not a reusable key\u003Cbr /\u003E\n\uD83D\uDC49 It is a \u003Cstrong\u003Eone-time step in a chain\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003EAccess Token Behavior\u003C/strong\u003E to understand how short-lived tokens interact with rotation.\u003C/p\u003E\n" + "Html": "\n\u003Cp\u003ERefresh tokens in UltimateAuth are not simple long-lived tokens.\u003C/p\u003E\n\u003Cp\u003EThey are part of a \u003Cstrong\u003Erotation-based security system\u003C/strong\u003E designed to:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eprevent token replay\u003C/li\u003E\n\u003Cli\u003Edetect token theft\u003C/li\u003E\n\u003Cli\u003Eenforce forward-only session progression\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022why-rotation\u0022\u003E\uD83E\uDDE0 Why Rotation?\u003C/h2\u003E\n\u003Cp\u003EIn traditional systems:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Erefresh tokens are long-lived\u003C/li\u003E\n\u003Cli\u003Ethey can be reused multiple times\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 If stolen, an attacker can:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Esilently keep refreshing access tokens\u003C/li\u003E\n\u003Cli\u003Estay undetected\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EUltimateAuth solves this with:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 \u003Cstrong\u003Esingle-use refresh tokens (rotation)\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022rotation-model\u0022\u003E\uD83D\uDD01 Rotation Model\u003C/h2\u003E\n\u003Cp\u003EEach refresh token is:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eused exactly once\u003C/li\u003E\n\u003Cli\u003Ereplaced with a new token\u003C/li\u003E\n\u003Cli\u003Elinked to a chain\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EToken A \u2192 Token B \u2192 Token C \u2192 ...\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EWhen a refresh happens:\u003C/p\u003E\n\u003Col\u003E\n\u003Cli\u003EToken A is validated\u003C/li\u003E\n\u003Cli\u003EToken A is \u003Cstrong\u003Erevoked\u003C/strong\u003E\u003C/li\u003E\n\u003Cli\u003EToken B is issued\u003C/li\u003E\n\u003Cli\u003EToken A is marked as replaced by B\u003C/li\u003E\n\u003C/ol\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022token-state\u0022\u003E\uD83D\uDD10 Token State\u003C/h2\u003E\n\u003Cp\u003EA refresh token can be:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EActive \u2192 valid and usable\u003C/li\u003E\n\u003Cli\u003ERevoked \u2192 already used or manually revoked\u003C/li\u003E\n\u003Cli\u003EExpired \u2192 lifetime exceeded\u003C/li\u003E\n\u003Cli\u003EReplaced \u2192 already rotated\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 Only \u003Cstrong\u003Eactive tokens\u003C/strong\u003E are valid\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022reuse-detection\u0022\u003E\uD83D\uDEA8 Reuse Detection\u003C/h2\u003E\n\u003Cp\u003EThis is the most critical security feature.\u003C/p\u003E\n\u003Cp\u003EIf a refresh token is used \u003Cstrong\u003Eafter it has already been rotated\u003C/strong\u003E, it means:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 The token was reused\u003Cbr /\u003E\n\uD83D\uDC49 Likely stolen\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022what-happens\u0022\u003EWhat happens?\u003C/h3\u003E\n\u003Cp\u003EWhen reuse is detected:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Ethe system identifies the session chain\u003C/li\u003E\n\u003Cli\u003Ethe entire chain can be revoked\u003C/li\u003E\n\u003Cli\u003Eall related sessions become invalid\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 This immediately cuts off both:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eattacker\u003C/li\u003E\n\u003Cli\u003Elegitimate user (forcing reauthentication)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022chain-awareness\u0022\u003E\uD83D\uDD17 Chain Awareness\u003C/h2\u003E\n\u003Cp\u003ERefresh tokens belong to a \u003Cstrong\u003Esession chain\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EThis enables:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Etracking rotation history\u003C/li\u003E\n\u003Cli\u003Edetecting anomalies\u003C/li\u003E\n\u003Cli\u003Eapplying revocation at the correct scope\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EWithout chains:\u003C/p\u003E\n\u003Cp\u003E\u274C You cannot safely detect reuse\u003Cbr /\u003E\n\u274C You cannot know which tokens belong together\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022rotation-flow\u0022\u003E\uD83D\uDD04 Rotation Flow\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EClient \u2192 Refresh(Token A)\n \u2192 Validate A\n \u2192 Revoke A\n \u2192 Issue B\n \u2192 Return new tokens\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022invalid-scenarios\u0022\u003E\u26A0\uFE0F Invalid Scenarios\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022expired-token\u0022\u003E1. Expired Token\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EToken expired \u2192 reject\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022revoked-token\u0022\u003E2. Revoked Token\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EToken already used \u2192 reuse detected\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022session-mismatch\u0022\u003E3. Session Mismatch\u003C/h3\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EToken does not belong to expected session \u2192 reject\n\u003C/code\u003E\u003C/pre\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-guarantees\u0022\u003E\uD83E\uDDE0 Security Guarantees\u003C/h2\u003E\n\u003Cp\u003ERotation ensures:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Erefresh tokens are forward-only\u003C/li\u003E\n\u003Cli\u003Eold tokens cannot be reused safely\u003C/li\u003E\n\u003Cli\u003Estolen tokens are detectable\u003C/li\u003E\n\u003Cli\u003Ecompromise triggers containment\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022why-this-matters\u0022\u003E\uD83D\uDD25 Why This Matters\u003C/h2\u003E\n\u003Cp\u003ECompared to traditional refresh tokens:\u003C/p\u003E\n\u003Ctable\u003E\n\u003Cthead\u003E\n\u003Ctr\u003E\n\u003Cth\u003EFeature\u003C/th\u003E\n\u003Cth\u003ETraditional\u003C/th\u003E\n\u003Cth\u003EUltimateAuth\u003C/th\u003E\n\u003C/tr\u003E\n\u003C/thead\u003E\n\u003Ctbody\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EReusable tokens\u003C/td\u003E\n\u003Ctd\u003E\u2705\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EReuse detection\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003E\u2705\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EChain awareness\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003E\u2705\u003C/td\u003E\n\u003C/tr\u003E\n\u003Ctr\u003E\n\u003Ctd\u003EAutomatic containment\u003C/td\u003E\n\u003Ctd\u003E\u274C\u003C/td\u003E\n\u003Ctd\u003E\u2705\u003C/td\u003E\n\u003C/tr\u003E\n\u003C/tbody\u003E\n\u003C/table\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022design-tradeoff\u0022\u003E\u26A0\uFE0F Design Tradeoff\u003C/h2\u003E\n\u003Cp\u003ERotation requires:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Etoken storage\u003C/li\u003E\n\u003Cli\u003Estate tracking\u003C/li\u003E\n\u003Cli\u003Eadditional validation logic\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\uD83D\uDC49 UltimateAuth chooses security over simplicity.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 A refresh token is not a reusable key\u003Cbr /\u003E\n\uD83D\uDC49 It is a \u003Cstrong\u003Eone-time step in a chain\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003EAccess Token Behavior\u003C/strong\u003E to understand how short-lived tokens interact with rotation.\u003C/p\u003E\n", + "Headings": [ + { + "Id": "why-rotation", + "Text": "\uD83E\uDDE0 Why Rotation?", + "Level": 0 + }, + { + "Id": "rotation-model", + "Text": "\uD83D\uDD01 Rotation Model", + "Level": 0 + }, + { + "Id": "token-state", + "Text": "\uD83D\uDD10 Token State", + "Level": 0 + }, + { + "Id": "reuse-detection", + "Text": "\uD83D\uDEA8 Reuse Detection", + "Level": 0 + }, + { + "Id": "what-happens", + "Text": "What happens?", + "Level": 1 + }, + { + "Id": "chain-awareness", + "Text": "\uD83D\uDD17 Chain Awareness", + "Level": 0 + }, + { + "Id": "rotation-flow", + "Text": "\uD83D\uDD04 Rotation Flow", + "Level": 0 + }, + { + "Id": "invalid-scenarios", + "Text": "\u26A0\uFE0F Invalid Scenarios", + "Level": 0 + }, + { + "Id": "expired-token", + "Text": "1. Expired Token", + "Level": 1 + }, + { + "Id": "revoked-token", + "Text": "2. Revoked Token", + "Level": 1 + }, + { + "Id": "session-mismatch", + "Text": "3. Session Mismatch", + "Level": 1 + }, + { + "Id": "security-guarantees", + "Text": "\uD83E\uDDE0 Security Guarantees", + "Level": 0 + }, + { + "Id": "why-this-matters", + "Text": "\uD83D\uDD25 Why This Matters", + "Level": 0 + }, + { + "Id": "design-tradeoff", + "Text": "\u26A0\uFE0F Design Tradeoff", + "Level": 0 + }, + { + "Id": "mental-model", + "Text": "\uD83E\uDDE0 Mental Model", + "Level": 0 + }, + { + "Id": "next-step", + "Text": "\u27A1\uFE0F Next Step", + "Level": 0 + } + ] } \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/session-security-model.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/session-security-model.json index 9a5415f3..abb996ca 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/session-security-model.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/security/session-security-model.json @@ -1,5 +1,97 @@ { "Slug": "security/session-security-model", "Title": "Session Security Model", - "Html": "\n\u003Cp\u003EUltimateAuth is built around a \u003Cstrong\u003Ehierarchical session model\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EThis is the foundation of its security design.\u003C/p\u003E\n\u003Ch2 id=\u0022why-a-session-model\u0022\u003E\uD83E\uDDE0 Why a Session Model?\u003C/h2\u003E\n\u003Cp\u003EMany systems treat authentication as one of these:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Ea cookie\u003C/li\u003E\n\u003Cli\u003Ea bearer token\u003C/li\u003E\n\u003Cli\u003Ea login flag\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThat works for simple apps.\u003C/p\u003E\n\u003Cp\u003EIt breaks down when you need:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eper-device isolation\u003C/li\u003E\n\u003Cli\u003Etargeted revocation\u003C/li\u003E\n\u003Cli\u003Esecurity state propagation\u003C/li\u003E\n\u003Cli\u003Ereliable reauthentication boundaries\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EUltimateAuth solves this by modeling authentication as:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ERoot \u2192 Chain \u2192 Session\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 id=\u0022the-three-layers\u0022\u003E\uD83E\uDDE9 The Three Layers\u003C/h2\u003E\n\u003Ch3 id=\u0022root\u0022\u003E\uD83D\uDD39 Root\u003C/h3\u003E\n\u003Cp\u003EA \u003Cstrong\u003ERoot\u003C/strong\u003E represents the user-level authentication authority.\u003C/p\u003E\n\u003Cp\u003EIt is:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Etenant-bound\u003C/li\u003E\n\u003Cli\u003Euser-bound\u003C/li\u003E\n\u003Cli\u003Elong-lived\u003C/li\u003E\n\u003Cli\u003Esecurity-versioned\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThe root is the highest-level trust anchor for authentication state.\u003C/p\u003E\n\u003Ch3 id=\u0022chain\u0022\u003E\uD83D\uDCF1 Chain\u003C/h3\u003E\n\u003Cp\u003EA \u003Cstrong\u003EChain\u003C/strong\u003E represents a device-level authentication boundary.\u003C/p\u003E\n\u003Cp\u003EIt groups sessions that belong to the same device or client context.\u003C/p\u003E\n\u003Cp\u003EA chain is where UltimateAuth models:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Edevice continuity\u003C/li\u003E\n\u003Cli\u003Etouch activity\u003C/li\u003E\n\u003Cli\u003Erotation tracking\u003C/li\u003E\n\u003Cli\u003Edevice-level revoke\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 id=\u0022session\u0022\u003E\uD83D\uDD11 Session\u003C/h3\u003E\n\u003Cp\u003EA \u003Cstrong\u003ESession\u003C/strong\u003E is a single authentication instance.\u003C/p\u003E\n\u003Cp\u003EIt is the most granular identity proof in the system.\u003C/p\u003E\n\u003Cp\u003EA session contains:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Ecreation time\u003C/li\u003E\n\u003Cli\u003Eexpiration time\u003C/li\u003E\n\u003Cli\u003Erevocation state\u003C/li\u003E\n\u003Cli\u003Esecurity version snapshot\u003C/li\u003E\n\u003Cli\u003Echain relationship\u003C/li\u003E\n\u003Cli\u003Edevice snapshot\u003C/li\u003E\n\u003Cli\u003Eclaims snapshot\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022relationship-model\u0022\u003E\uD83D\uDD17 Relationship Model\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EUser\n \u2514\u2500\u2500 Root\n \u2514\u2500\u2500 Chain (device)\n \u2514\u2500\u2500 Session (login instance)\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Root answers: \u003Cstrong\u003Ewhat is the current security authority for this user?\u003C/strong\u003E\u003Cbr /\u003E\n\uD83D\uDC49 Chain answers: \u003Cstrong\u003Ewhich device context is this?\u003C/strong\u003E\u003Cbr /\u003E\n\uD83D\uDC49 Session answers: \u003Cstrong\u003Ewhich authentication instance is being used?\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022security-versioning\u0022\u003E\uD83D\uDEE1 Security Versioning\u003C/h2\u003E\n\u003Cp\u003EOne of the most important protections in UltimateAuth is \u003Cstrong\u003Esecurity versioning\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EA root maintains a security version.\u003C/p\u003E\n\u003Cp\u003EEach session stores the security version that existed at creation time.\u003C/p\u003E\n\u003Cp\u003EValidation compares them.\u003C/p\u003E\n\u003Cp\u003EIf they no longer match:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ESession \u2192 invalid\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThis is how UltimateAuth can invalidate existing sessions after events such as:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Epassword change\u003C/li\u003E\n\u003Cli\u003Ecredential reset\u003C/li\u003E\n\u003Cli\u003Eaccount recovery\u003C/li\u003E\n\u003Cli\u003Eadministrative security action\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022validation-model\u0022\u003E\uD83D\uDD0D Validation Model\u003C/h2\u003E\n\u003Cp\u003ESession validation is not a single check.\u003C/p\u003E\n\u003Cp\u003EIt is a layered verification process.\u003C/p\u003E\n\u003Cp\u003EA session is considered valid only if all of these still hold:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Ethe session exists\u003C/li\u003E\n\u003Cli\u003Ethe session is active\u003C/li\u003E\n\u003Cli\u003Ethe chain exists\u003C/li\u003E\n\u003Cli\u003Ethe chain is active\u003C/li\u003E\n\u003Cli\u003Ethe chain belongs to the expected tenant\u003C/li\u003E\n\u003Cli\u003Ethe chain matches the session\u003C/li\u003E\n\u003Cli\u003Ethe root exists\u003C/li\u003E\n\u003Cli\u003Ethe root is not revoked\u003C/li\u003E\n\u003Cli\u003Ethe root matches the chain\u003C/li\u003E\n\u003Cli\u003Ethe root security version matches the session snapshot\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022device-awareness\u0022\u003E\uD83D\uDCF1 Device Awareness\u003C/h2\u003E\n\u003Cp\u003EChains provide device-level isolation.\u003C/p\u003E\n\u003Cp\u003EWhen a device identifier is available, validation can compare:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Edevice stored on chain\u003C/li\u003E\n\u003Cli\u003Edevice presented by request\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EIf they do not match, UltimateAuth can reject the request depending on configuration.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 This makes device-bound security enforceable without turning every authentication flow into a custom implementation.\u003C/p\u003E\n\u003Ch2 id=\u0022expiration-and-activity\u0022\u003E\u23F1 Expiration and Activity\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth separates:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Esession expiration\u003C/li\u003E\n\u003Cli\u003Echain activity\u003C/li\u003E\n\u003Cli\u003Eroot security state\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThis is important.\u003C/p\u003E\n\u003Cp\u003EA session may expire because of time.\u003Cbr /\u003E\nA chain may become inactive because of idle timeout.\u003Cbr /\u003E\nA root may invalidate everything because security state changed.\u003C/p\u003E\n\u003Cp\u003EThese are different failure modes with different meanings.\u003C/p\u003E\n\u003Ch2 id=\u0022revocation-boundaries\u0022\u003E\uD83D\uDEAA Revocation Boundaries\u003C/h2\u003E\n\u003Cp\u003ERevocation is also hierarchical.\u003C/p\u003E\n\u003Ch3 id=\u0022session-revoke\u0022\u003ESession revoke\u003C/h3\u003E\n\u003Cp\u003EInvalidates one authentication instance.\u003C/p\u003E\n\u003Ch3 id=\u0022chain-revoke\u0022\u003EChain revoke\u003C/h3\u003E\n\u003Cp\u003EInvalidates all sessions for one device.\u003C/p\u003E\n\u003Ch3 id=\u0022root-revoke\u0022\u003ERoot revoke\u003C/h3\u003E\n\u003Cp\u003EInvalidates all chains and sessions for the user.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 This gives UltimateAuth targeted containment.\u003C/p\u003E\n\u003Cp\u003EInstead of \u201Clog out everywhere or nowhere,\u201D\u003Cbr /\u003E\nyou can revoke exactly the right scope.\u003C/p\u003E\n\u003Ch2 id=\u0022why-this-matters\u0022\u003E\uD83D\uDD25 Why This Matters\u003C/h2\u003E\n\u003Cp\u003EThis model gives you controls that flat token systems usually do not provide:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Erevoke one device without affecting others\u003C/li\u003E\n\u003Cli\u003Einvalidate sessions after security changes\u003C/li\u003E\n\u003Cli\u003Ereason about device trust explicitly\u003C/li\u003E\n\u003Cli\u003Eseparate authentication lifetime from token lifetime\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022design-tradeoff\u0022\u003E\u26A0\uFE0F Design Tradeoff\u003C/h2\u003E\n\u003Cp\u003EThis model is more sophisticated than plain JWT-only authentication.\u003C/p\u003E\n\u003Cp\u003EThat is intentional.\u003C/p\u003E\n\u003Cp\u003EUltimateAuth chooses:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eexplicit state\u003C/li\u003E\n\u003Cli\u003Erevocability\u003C/li\u003E\n\u003Cli\u003Etraceable security decisions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003Eover:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eminimal infrastructure\u003C/li\u003E\n\u003Cli\u003Epurely stateless assumptions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Authentication in UltimateAuth is not \u201Ca token\u201D\u003Cbr /\u003E\n\uD83D\uDC49 It is a \u003Cstrong\u003Esecurity hierarchy with revocation boundaries\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003ERefresh Token Rotation\u003C/strong\u003E to understand how continuation security works after login.\u003C/p\u003E\n" + "Html": "\n\u003Cp\u003EUltimateAuth is built around a \u003Cstrong\u003Ehierarchical session model\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EThis is the foundation of its security design.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022why-a-session-model\u0022\u003E\uD83E\uDDE0 Why a Session Model?\u003C/h2\u003E\n\u003Cp\u003EMany systems treat authentication as one of these:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Ea cookie\u003C/li\u003E\n\u003Cli\u003Ea bearer token\u003C/li\u003E\n\u003Cli\u003Ea login flag\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThat works for simple apps.\u003C/p\u003E\n\u003Cp\u003EIt breaks down when you need:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eper-device isolation\u003C/li\u003E\n\u003Cli\u003Etargeted revocation\u003C/li\u003E\n\u003Cli\u003Esecurity state propagation\u003C/li\u003E\n\u003Cli\u003Ereliable reauthentication boundaries\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EUltimateAuth solves this by modeling authentication as:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ERoot \u2192 Chain \u2192 Session\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cbr\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022the-three-layers\u0022\u003E\uD83E\uDDE9 The Three Layers\u003C/h2\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022root\u0022\u003E\uD83D\uDD39 Root\u003C/h3\u003E\n\u003Cp\u003EA \u003Cstrong\u003ERoot\u003C/strong\u003E represents the user-level authentication authority.\u003C/p\u003E\n\u003Cp\u003EIt is:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Etenant-bound\u003C/li\u003E\n\u003Cli\u003Euser-bound\u003C/li\u003E\n\u003Cli\u003Elong-lived\u003C/li\u003E\n\u003Cli\u003Esecurity-versioned\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThe root is the highest-level trust anchor for authentication state.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022chain\u0022\u003E\uD83D\uDCF1 Chain\u003C/h3\u003E\n\u003Cp\u003EA \u003Cstrong\u003EChain\u003C/strong\u003E represents a device-level authentication boundary.\u003C/p\u003E\n\u003Cp\u003EIt groups sessions that belong to the same device or client context.\u003C/p\u003E\n\u003Cp\u003EA chain is where UltimateAuth models:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Edevice continuity\u003C/li\u003E\n\u003Cli\u003Etouch activity\u003C/li\u003E\n\u003Cli\u003Erotation tracking\u003C/li\u003E\n\u003Cli\u003Edevice-level revoke\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022session\u0022\u003E\uD83D\uDD11 Session\u003C/h3\u003E\n\u003Cp\u003EA \u003Cstrong\u003ESession\u003C/strong\u003E is a single authentication instance.\u003C/p\u003E\n\u003Cp\u003EIt is the most granular identity proof in the system.\u003C/p\u003E\n\u003Cp\u003EA session contains:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Ecreation time\u003C/li\u003E\n\u003Cli\u003Eexpiration time\u003C/li\u003E\n\u003Cli\u003Erevocation state\u003C/li\u003E\n\u003Cli\u003Esecurity version snapshot\u003C/li\u003E\n\u003Cli\u003Echain relationship\u003C/li\u003E\n\u003Cli\u003Edevice snapshot\u003C/li\u003E\n\u003Cli\u003Eclaims snapshot\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022relationship-model\u0022\u003E\uD83D\uDD17 Relationship Model\u003C/h2\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003EUser\n \u2514\u2500\u2500 Root\n \u2514\u2500\u2500 Chain (device)\n \u2514\u2500\u2500 Session (login instance)\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003E\uD83D\uDC49 Root answers: \u003Cstrong\u003Ewhat is the current security authority for this user?\u003C/strong\u003E\u003Cbr /\u003E\n\uD83D\uDC49 Chain answers: \u003Cstrong\u003Ewhich device context is this?\u003C/strong\u003E\u003Cbr /\u003E\n\uD83D\uDC49 Session answers: \u003Cstrong\u003Ewhich authentication instance is being used?\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022security-versioning\u0022\u003E\uD83D\uDEE1 Security Versioning\u003C/h2\u003E\n\u003Cp\u003EOne of the most important protections in UltimateAuth is \u003Cstrong\u003Esecurity versioning\u003C/strong\u003E.\u003C/p\u003E\n\u003Cp\u003EA root maintains a security version.\u003C/p\u003E\n\u003Cp\u003EEach session stores the security version that existed at creation time.\u003C/p\u003E\n\u003Cp\u003EValidation compares them.\u003C/p\u003E\n\u003Cp\u003EIf they no longer match:\u003C/p\u003E\n\u003Cpre\u003E\u003Ccode class=\u0022language-text\u0022\u003ESession \u2192 invalid\n\u003C/code\u003E\u003C/pre\u003E\n\u003Cp\u003EThis is how UltimateAuth can invalidate existing sessions after events such as:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Epassword change\u003C/li\u003E\n\u003Cli\u003Ecredential reset\u003C/li\u003E\n\u003Cli\u003Eaccount recovery\u003C/li\u003E\n\u003Cli\u003Eadministrative security action\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022validation-model\u0022\u003E\uD83D\uDD0D Validation Model\u003C/h2\u003E\n\u003Cp\u003ESession validation is not a single check.\u003C/p\u003E\n\u003Cp\u003EIt is a layered verification process.\u003C/p\u003E\n\u003Cp\u003EA session is considered valid only if all of these still hold:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Ethe session exists\u003C/li\u003E\n\u003Cli\u003Ethe session is active\u003C/li\u003E\n\u003Cli\u003Ethe chain exists\u003C/li\u003E\n\u003Cli\u003Ethe chain is active\u003C/li\u003E\n\u003Cli\u003Ethe chain belongs to the expected tenant\u003C/li\u003E\n\u003Cli\u003Ethe chain matches the session\u003C/li\u003E\n\u003Cli\u003Ethe root exists\u003C/li\u003E\n\u003Cli\u003Ethe root is not revoked\u003C/li\u003E\n\u003Cli\u003Ethe root matches the chain\u003C/li\u003E\n\u003Cli\u003Ethe root security version matches the session snapshot\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022device-awareness\u0022\u003E\uD83D\uDCF1 Device Awareness\u003C/h2\u003E\n\u003Cp\u003EChains provide device-level isolation.\u003C/p\u003E\n\u003Cp\u003EWhen a device identifier is available, validation can compare:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Edevice stored on chain\u003C/li\u003E\n\u003Cli\u003Edevice presented by request\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EIf they do not match, UltimateAuth can reject the request depending on configuration.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 This makes device-bound security enforceable without turning every authentication flow into a custom implementation.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022expiration-and-activity\u0022\u003E\u23F1 Expiration and Activity\u003C/h2\u003E\n\u003Cp\u003EUltimateAuth separates:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Esession expiration\u003C/li\u003E\n\u003Cli\u003Echain activity\u003C/li\u003E\n\u003Cli\u003Eroot security state\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003EThis is important.\u003C/p\u003E\n\u003Cp\u003EA session may expire because of time.\u003Cbr /\u003E\nA chain may become inactive because of idle timeout.\u003Cbr /\u003E\nA root may invalidate everything because security state changed.\u003C/p\u003E\n\u003Cp\u003EThese are different failure modes with different meanings.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022revocation-boundaries\u0022\u003E\uD83D\uDEAA Revocation Boundaries\u003C/h2\u003E\n\u003Cp\u003ERevocation is also hierarchical.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022session-revoke\u0022\u003ESession revoke\u003C/h3\u003E\n\u003Cp\u003EInvalidates one authentication instance.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022chain-revoke\u0022\u003EChain revoke\u003C/h3\u003E\n\u003Cp\u003EInvalidates all sessions for one device.\u003C/p\u003E\n\u003Ch3 class=\u0022mud-scrollspy-section\u0022 id=\u0022root-revoke\u0022\u003ERoot revoke\u003C/h3\u003E\n\u003Cp\u003EInvalidates all chains and sessions for the user.\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 This gives UltimateAuth targeted containment.\u003C/p\u003E\n\u003Cp\u003EInstead of \u201Clog out everywhere or nowhere,\u201D\u003Cbr /\u003E\nyou can revoke exactly the right scope.\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022why-this-matters\u0022\u003E\uD83D\uDD25 Why This Matters\u003C/h2\u003E\n\u003Cp\u003EThis model gives you controls that flat token systems usually do not provide:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Erevoke one device without affecting others\u003C/li\u003E\n\u003Cli\u003Einvalidate sessions after security changes\u003C/li\u003E\n\u003Cli\u003Ereason about device trust explicitly\u003C/li\u003E\n\u003Cli\u003Eseparate authentication lifetime from token lifetime\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022design-tradeoff\u0022\u003E\u26A0\uFE0F Design Tradeoff\u003C/h2\u003E\n\u003Cp\u003EThis model is more sophisticated than plain JWT-only authentication.\u003C/p\u003E\n\u003Cp\u003EThat is intentional.\u003C/p\u003E\n\u003Cp\u003EUltimateAuth chooses:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eexplicit state\u003C/li\u003E\n\u003Cli\u003Erevocability\u003C/li\u003E\n\u003Cli\u003Etraceable security decisions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003Eover:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003Eminimal infrastructure\u003C/li\u003E\n\u003Cli\u003Epurely stateless assumptions\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022mental-model\u0022\u003E\uD83E\uDDE0 Mental Model\u003C/h2\u003E\n\u003Cp\u003EIf you remember one thing:\u003C/p\u003E\n\u003Cp\u003E\uD83D\uDC49 Authentication in UltimateAuth is not \u201Ca token\u201D\u003Cbr /\u003E\n\uD83D\uDC49 It is a \u003Cstrong\u003Esecurity hierarchy with revocation boundaries\u003C/strong\u003E\u003C/p\u003E\n\u003Ch2 class=\u0022mud-scrollspy-section\u0022 id=\u0022next-step\u0022\u003E\u27A1\uFE0F Next Step\u003C/h2\u003E\n\u003Cp\u003EContinue to \u003Cstrong\u003ERefresh Token Rotation\u003C/strong\u003E to understand how continuation security works after login.\u003C/p\u003E\n", + "Headings": [ + { + "Id": "why-a-session-model", + "Text": "\uD83E\uDDE0 Why a Session Model?", + "Level": 0 + }, + { + "Id": "the-three-layers", + "Text": "\uD83E\uDDE9 The Three Layers", + "Level": 0 + }, + { + "Id": "root", + "Text": "\uD83D\uDD39 Root", + "Level": 1 + }, + { + "Id": "chain", + "Text": "\uD83D\uDCF1 Chain", + "Level": 1 + }, + { + "Id": "session", + "Text": "\uD83D\uDD11 Session", + "Level": 1 + }, + { + "Id": "relationship-model", + "Text": "\uD83D\uDD17 Relationship Model", + "Level": 0 + }, + { + "Id": "security-versioning", + "Text": "\uD83D\uDEE1 Security Versioning", + "Level": 0 + }, + { + "Id": "validation-model", + "Text": "\uD83D\uDD0D Validation Model", + "Level": 0 + }, + { + "Id": "device-awareness", + "Text": "\uD83D\uDCF1 Device Awareness", + "Level": 0 + }, + { + "Id": "expiration-and-activity", + "Text": "\u23F1 Expiration and Activity", + "Level": 0 + }, + { + "Id": "revocation-boundaries", + "Text": "\uD83D\uDEAA Revocation Boundaries", + "Level": 0 + }, + { + "Id": "session-revoke", + "Text": "Session revoke", + "Level": 1 + }, + { + "Id": "chain-revoke", + "Text": "Chain revoke", + "Level": 1 + }, + { + "Id": "root-revoke", + "Text": "Root revoke", + "Level": 1 + }, + { + "Id": "why-this-matters", + "Text": "\uD83D\uDD25 Why This Matters", + "Level": 0 + }, + { + "Id": "design-tradeoff", + "Text": "\u26A0\uFE0F Design Tradeoff", + "Level": 0 + }, + { + "Id": "mental-model", + "Text": "\uD83E\uDDE0 Mental Model", + "Level": 0 + }, + { + "Id": "next-step", + "Text": "\u27A1\uFE0F Next Step", + "Level": 0 + } + ] } \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/Components/App.razor b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/Components/App.razor index 4c724313..8534df7b 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/Components/App.razor +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/Components/App.razor @@ -37,6 +37,7 @@ + diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/Program.cs b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/Program.cs index 247e48c6..01e3ec41 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/Program.cs +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/Program.cs @@ -12,6 +12,7 @@ builder.Services.AddMudExtensions(); builder.Services.AddHttpClient(); +builder.Services.AddScoped(); var app = builder.Build(); diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/wwwroot/app.css b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/wwwroot/app.css index 658dbe25..9811aa15 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/wwwroot/app.css +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/wwwroot/app.css @@ -254,3 +254,21 @@ h1:focus { justify-content: center; margin-top: 100px; } + +.docs-toc-container { + position: sticky; + top: 80px; + height: fit-content; +} + +.toc-active { + font-weight: 600; + color: var(--mud-palette-primary); + border-left: 2px solid var(--mud-palette-primary); + background: var(--mud-palette-background-gray); +} + +.docs-content h2, +.docs-content h3 { + scroll-margin-top: 240px; +} diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/wwwroot/docs.js b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/wwwroot/docs.js new file mode 100644 index 00000000..8d695850 --- /dev/null +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/wwwroot/docs.js @@ -0,0 +1,73 @@ +window.uaDocsScrollSpy = (() => { + + let lastId = null; + let handler = null; + + function start(dotnetRef) { + + stop(); + + handler = () => { + + const elements = document.querySelectorAll(".docs-content h2, .docs-content h3"); + if (!elements.length) return; + + const center = window.innerHeight / 2; + + let best = null; + let minDiff = Number.MAX_SAFE_INTEGER; + + for (const el of elements) { + const rect = el.getBoundingClientRect(); + const diff = Math.abs(rect.top - center); + + if (rect.top <= center && diff < minDiff) { + minDiff = diff; + best = el; + } + } + + if (!best) return; + + const id = best.id; + + if (id !== lastId) { + lastId = id; + history.replaceState(null, "", window.location.pathname + "#" + id); + dotnetRef.invokeMethodAsync("SetActiveHeading", id); + } + }; + + document.addEventListener('scroll', handler, true); + window.addEventListener('resize', handler, true); + + handler(); + } + + function stop() { + if (handler) { + document.removeEventListener('scroll', handler, true); + window.removeEventListener('resize', handler, true); + handler = null; + } + } + + function scrollTo(id) { + const el = document.getElementById(id); + if (!el) return; + + const offset = 300; + const y = window.scrollY + el.getBoundingClientRect().top - offset; + + window.scrollTo({ + top: y, + behavior: "smooth" + }); + } + + return { + start, + stop, + scrollTo + }; +})(); diff --git a/src/utilities/CodeBeam.UltimateAuth.DocsBuilder/CodeBeam.UltimateAuth.DocsBuilder.csproj b/src/utilities/CodeBeam.UltimateAuth.DocsBuilder/CodeBeam.UltimateAuth.DocsBuilder.csproj index 4bdfdb9f..15919d0a 100644 --- a/src/utilities/CodeBeam.UltimateAuth.DocsBuilder/CodeBeam.UltimateAuth.DocsBuilder.csproj +++ b/src/utilities/CodeBeam.UltimateAuth.DocsBuilder/CodeBeam.UltimateAuth.DocsBuilder.csproj @@ -9,6 +9,7 @@ + diff --git a/src/utilities/CodeBeam.UltimateAuth.DocsBuilder/Program.cs b/src/utilities/CodeBeam.UltimateAuth.DocsBuilder/Program.cs index 09c1791a..4fee7631 100644 --- a/src/utilities/CodeBeam.UltimateAuth.DocsBuilder/Program.cs +++ b/src/utilities/CodeBeam.UltimateAuth.DocsBuilder/Program.cs @@ -1,5 +1,8 @@ -using System.Text.Json; +using HtmlAgilityPack; using Markdig; +using System.Text.Json; +using System.Text.RegularExpressions; +using System.Xml; Console.WriteLine("=== DocsBuilder START ==="); @@ -39,6 +42,7 @@ var pipeline = new MarkdownPipelineBuilder() .UseAdvancedExtensions() + .UseAutoIdentifiers() .Build(); var markdownFiles = Directory.GetFiles(sourceDocsDir, "*.md", SearchOption.AllDirectories); @@ -97,14 +101,20 @@ Console.WriteLine($"⚙ Processing: {relativePath}"); + //var headings = ExtractHeadings(content); var html = Markdown.ToHtml(content, pipeline); html = RemoveFirstH1(html); + var headings = ExtractHeadingsFromHtml(html); + + html = Regex.Replace(html, " ExtractHeadings(string markdown) +{ + var result = new List(); + using var reader = new StringReader(markdown); + + string? line; + while ((line = reader.ReadLine()) is not null) + { + var trimmed = line.Trim(); + + if (trimmed.StartsWith("## ")) + { + var text = trimmed[3..].Trim(); + result.Add(new DocsHeadingItem + { + Text = text, + Id = Slugify(text), + Level = 0 + }); + } + else if (trimmed.StartsWith("### ")) + { + var text = trimmed[4..].Trim(); + result.Add(new DocsHeadingItem + { + Text = text, + Id = Slugify(text), + Level = 1 + }); + } + } + + return result; +} + +static List ExtractHeadingsFromHtml(string html) +{ + var result = new List(); + + var doc = new HtmlDocument(); + doc.LoadHtml(html); + + var nodes = doc.DocumentNode.SelectNodes("//h2 | //h3"); + if (nodes == null) + return result; + + foreach (var node in nodes) + { + var id = node.GetAttributeValue("id", null); + + if (string.IsNullOrWhiteSpace(id)) + continue; + + result.Add(new DocsHeadingItem + { + Id = id, + Text = HtmlEntity.DeEntitize(node.InnerText), + Level = node.Name == "h2" ? 0 : 1 + }); + } + + return result; +} + +static string Slugify(string text) +{ + var chars = text + .ToLowerInvariant() + .Select(c => char.IsLetterOrDigit(c) ? c : '-') + .ToArray(); + + var slug = new string(chars); + while (slug.Contains("--")) + slug = slug.Replace("--", "-"); + + return slug.Trim('-'); +} + +internal sealed record DocOutput(string Slug, string Title, string Html, List Headings); internal sealed class DocIndexItem { @@ -248,3 +336,10 @@ internal sealed class DocIndexItem public string Group { get; set; } = ""; public int GroupOrder { get; set; } = 999; } + +internal sealed class DocsHeadingItem +{ + public string Id { get; set; } = ""; + public string Text { get; set; } = ""; + public int Level { get; set; } +} From f34984b55e4f587e66bf48eca251b3c09746b99a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Can=20Karag=C3=B6z?= Date: Thu, 9 Apr 2026 23:34:16 +0300 Subject: [PATCH 4/4] Fix Fundamentals Index Page Parse Issue --- docs/content/fundamentals/index.md | 38 ++----- ...eBeam.UltimateAuth.Docs.Wasm.Client.csproj | 2 + .../Pages/DocsSidebar.razor | 4 +- .../Pages/DocsToc.razor | 6 +- .../wwwroot/docs/docs-index.json | 14 +-- .../wwwroot/docs/fundamentals/index.json | 4 +- .../Program.cs | 104 ++++-------------- 7 files changed, 46 insertions(+), 126 deletions(-) diff --git a/docs/content/fundamentals/index.md b/docs/content/fundamentals/index.md index e757b828..fbe6c93b 100644 --- a/docs/content/fundamentals/index.md +++ b/docs/content/fundamentals/index.md @@ -1,40 +1,22 @@ -\--- - +--- title: Fundamental Overview - order: 1 - group: fundamentals +--- -\--- - - - - - -\# 🧠 Fundamental Overview - +# Overview This section explains how UltimateAuth works internally. - -\## Section Order +# Section Order If you are new, follow this order: - - -\- Authentication Model - -\- Flow-Based Authentication - -\- Authentication Modes - -\- Client Profiles - -\- Runtime Architecture - -\- Request Lifecycle - +- Authentication Model +- Flow-Based Authentication +- Authentication Modes +- Client Profiles +- Runtime Architecture +- Request Lifecycle diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/CodeBeam.UltimateAuth.Docs.Wasm.Client.csproj b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/CodeBeam.UltimateAuth.Docs.Wasm.Client.csproj index db9bf894..3ac3c530 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/CodeBeam.UltimateAuth.Docs.Wasm.Client.csproj +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/CodeBeam.UltimateAuth.Docs.Wasm.Client.csproj @@ -20,5 +20,7 @@ + + diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsSidebar.razor b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsSidebar.razor index 030151ba..08c0b652 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsSidebar.razor +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsSidebar.razor @@ -1,7 +1,7 @@ @inject HttpClient Http @inject NavigationManager Nav -
+
@if (!Inline) { @@ -18,7 +18,7 @@ } else { - + Overview @foreach (var group in _groups) { diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsToc.razor b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsToc.razor index dae31396..700b47d5 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsToc.razor +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsToc.razor @@ -13,7 +13,7 @@ @foreach (var item in PageState.Headings) { - @item.Text @@ -31,12 +31,8 @@ protected override async Task OnAfterRenderAsync(bool firstRender) { - Console.WriteLine($"OnAfterRender - Headings: {PageState.Headings.Count}"); - if (_ref == null && PageState.Headings.Count > 0) { - Console.WriteLine("Starting ScrollSpy"); - _ref = DotNetObjectReference.Create(this); await JS.InvokeVoidAsync("uaDocsScrollSpy.start", _ref); } diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/docs-index.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/docs-index.json index 2216ca65..ae4a7783 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/docs-index.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/docs-index.json @@ -20,6 +20,13 @@ "Group": "getting-started", "GroupOrder": 1 }, + { + "Title": "Fundamental Overview", + "Slug": "fundamentals/index", + "Order": 1, + "Group": "fundamentals", + "GroupOrder": 2 + }, { "Title": "Authentication Model", "Slug": "fundamentals/auth-model", @@ -271,12 +278,5 @@ "Order": 999, "Group": "", "GroupOrder": 999 - }, - { - "Title": "index", - "Slug": "fundamentals/index", - "Order": 999, - "Group": "", - "GroupOrder": 999 } ] \ No newline at end of file diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/index.json b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/index.json index d973d5eb..d5c1b23a 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/index.json +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/wwwroot/docs/fundamentals/index.json @@ -1,6 +1,6 @@ { "Slug": "fundamentals/index", - "Title": "index", - "Html": "\u003Cp\u003E---\u003C/p\u003E\n\u003Cp\u003Etitle: Fundamental Overview\u003C/p\u003E\n\u003Cp\u003Eorder: 1\u003C/p\u003E\n\u003Cp\u003Egroup: fundamentals\u003C/p\u003E\n\u003Cp\u003E---\u003C/p\u003E\n\u003Cp\u003E# \uD83E\uDDE0 Fundamental Overview\u003C/p\u003E\n\u003Cp\u003EThis section explains how UltimateAuth works internally.\u003C/p\u003E\n\u003Cp\u003E## Section Order\u003C/p\u003E\n\u003Cp\u003EIf you are new, follow this order:\u003C/p\u003E\n\u003Cp\u003E- Authentication Model\u003C/p\u003E\n\u003Cp\u003E- Flow-Based Authentication\u003C/p\u003E\n\u003Cp\u003E- Authentication Modes\u003C/p\u003E\n\u003Cp\u003E- Client Profiles\u003C/p\u003E\n\u003Cp\u003E- Runtime Architecture\u003C/p\u003E\n\u003Cp\u003E- Request Lifecycle\u003C/p\u003E\n", + "Title": "Fundamental Overview", + "Html": "\n\u003Cp\u003EThis section explains how UltimateAuth works internally.\u003C/p\u003E\n\u003Ch1 id=\u0022section-order\u0022\u003ESection Order\u003C/h1\u003E\n\u003Cp\u003EIf you are new, follow this order:\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EAuthentication Model\u003C/li\u003E\n\u003Cli\u003EFlow-Based Authentication\u003C/li\u003E\n\u003Cli\u003EAuthentication Modes\u003C/li\u003E\n\u003Cli\u003EClient Profiles\u003C/li\u003E\n\u003Cli\u003ERuntime Architecture\u003C/li\u003E\n\u003Cli\u003ERequest Lifecycle\u003C/li\u003E\n\u003C/ul\u003E\n", "Headings": [] } \ No newline at end of file diff --git a/src/utilities/CodeBeam.UltimateAuth.DocsBuilder/Program.cs b/src/utilities/CodeBeam.UltimateAuth.DocsBuilder/Program.cs index 4fee7631..0b6de37a 100644 --- a/src/utilities/CodeBeam.UltimateAuth.DocsBuilder/Program.cs +++ b/src/utilities/CodeBeam.UltimateAuth.DocsBuilder/Program.cs @@ -101,7 +101,6 @@ Console.WriteLine($"⚙ Processing: {relativePath}"); - //var headings = ExtractHeadings(content); var html = Markdown.ToHtml(content, pipeline); html = RemoveFirstH1(html); @@ -148,13 +147,12 @@ await File.WriteAllTextAsync( static string? ExtractTitle(string markdown) { using var reader = new StringReader(markdown); - while (reader.ReadLine() is { } line) { - if (line.StartsWith("# ")) - return line[2..].Trim(); + var trimmed = line.Trim(); + if (trimmed.StartsWith("# ")) + return trimmed[2..].Trim(); } - return null; } @@ -197,43 +195,34 @@ static string FindSolutionRootFallback() static (Dictionary meta, string content) ParseFrontmatter(string markdown) { - if (string.IsNullOrWhiteSpace(markdown)) - return (new(), string.Empty); - - markdown = markdown.TrimStart('\uFEFF'); - markdown = markdown.Replace("\r\n", "\n").Replace("\r", "\n"); - markdown = markdown.TrimStart(); - - if (!markdown.StartsWith("---")) - return (new(), markdown); - - var firstLineEnd = markdown.IndexOf('\n'); - if (firstLineEnd == -1) - return (new(), markdown); - - var firstLine = markdown.Substring(0, firstLineEnd).Trim(); - if (firstLine != "---") - return (new(), markdown); + var dict = new Dictionary(StringComparer.OrdinalIgnoreCase); + if (string.IsNullOrWhiteSpace(markdown)) return (dict, string.Empty); - var end = markdown.IndexOf("\n---\n", firstLineEnd + 1); - if (end == -1) - return (new(), markdown); + var lines = markdown.Replace("\r\n", "\n").Replace("\r", "\n").Split('\n'); - var rawMeta = markdown.Substring(firstLineEnd + 1, end - firstLineEnd - 1); - var content = markdown[(end + 5)..]; + if (lines.Length == 0 || lines[0].Trim() != "---") + return (dict, markdown); - var dict = new Dictionary(StringComparer.OrdinalIgnoreCase); - - foreach (var line in rawMeta.Split('\n', StringSplitOptions.RemoveEmptyEntries)) + int closingIndex = -1; + for (int i = 1; i < lines.Length; i++) { - var parts = line.Split(':', 2, StringSplitOptions.TrimEntries); + if (lines[i].Trim() == "---") + { + closingIndex = i; + break; + } - if (parts.Length == 2 && !string.IsNullOrWhiteSpace(parts[0])) + var parts = lines[i].Split(':', 2, StringSplitOptions.TrimEntries); + if (parts.Length == 2) { dict[parts[0]] = parts[1]; } } + if (closingIndex == -1) + return (new(), markdown); + + var content = string.Join("\n", lines.Skip(closingIndex + 1)); return (dict, content); } @@ -248,41 +237,6 @@ static string RemoveFirstH1(string html) return html.Remove(start, end + 5 - start); } -static List ExtractHeadings(string markdown) -{ - var result = new List(); - using var reader = new StringReader(markdown); - - string? line; - while ((line = reader.ReadLine()) is not null) - { - var trimmed = line.Trim(); - - if (trimmed.StartsWith("## ")) - { - var text = trimmed[3..].Trim(); - result.Add(new DocsHeadingItem - { - Text = text, - Id = Slugify(text), - Level = 0 - }); - } - else if (trimmed.StartsWith("### ")) - { - var text = trimmed[4..].Trim(); - result.Add(new DocsHeadingItem - { - Text = text, - Id = Slugify(text), - Level = 1 - }); - } - } - - return result; -} - static List ExtractHeadingsFromHtml(string html) { var result = new List(); @@ -296,7 +250,7 @@ static List ExtractHeadingsFromHtml(string html) foreach (var node in nodes) { - var id = node.GetAttributeValue("id", null); + var id = node.GetAttributeValue("id", string.Empty); if (string.IsNullOrWhiteSpace(id)) continue; @@ -312,20 +266,6 @@ static List ExtractHeadingsFromHtml(string html) return result; } -static string Slugify(string text) -{ - var chars = text - .ToLowerInvariant() - .Select(c => char.IsLetterOrDigit(c) ? c : '-') - .ToArray(); - - var slug = new string(chars); - while (slug.Contains("--")) - slug = slug.Replace("--", "-"); - - return slug.Trim('-'); -} - internal sealed record DocOutput(string Slug, string Title, string Html, List Headings); internal sealed class DocIndexItem