{"id":23120280,"url":"https://github.com/dragos-tudor/backend-security","last_synced_at":"2025-04-04T03:14:55.713Z","repository":{"id":211729551,"uuid":"729069112","full_name":"dragos-tudor/backend-security","owner":"dragos-tudor","description":"Rewritten ASPNET security libraries [functional principles].","archived":false,"fork":false,"pushed_at":"2025-02-06T15:08:26.000Z","size":917,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-09T14:46:25.650Z","etag":null,"topics":["aspnet","authentication","authorization","cookies","data-protection","facebook-auth","functional","functional-programming","google-auth","oauth2","openid-connect","security","twitter-auth"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dragos-tudor.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"Security.Authentication.BearerToken/AccessTokenResponseJson/AccessTokenResponseJsonMetadata.cs","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-12-08T10:38:53.000Z","updated_at":"2025-02-06T15:08:30.000Z","dependencies_parsed_at":"2024-02-05T17:50:10.646Z","dependency_job_id":"d682ca43-77d3-470e-8182-1820500520a8","html_url":"https://github.com/dragos-tudor/backend-security","commit_stats":null,"previous_names":["dragos-tudor/backend-security"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dragos-tudor%2Fbackend-security","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dragos-tudor%2Fbackend-security/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dragos-tudor%2Fbackend-security/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dragos-tudor%2Fbackend-security/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dragos-tudor","download_url":"https://codeload.github.com/dragos-tudor/backend-security/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247112770,"owners_count":20885606,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aspnet","authentication","authorization","cookies","data-protection","facebook-auth","functional","functional-programming","google-auth","oauth2","openid-connect","security","twitter-auth"],"created_at":"2024-12-17T06:09:38.089Z","updated_at":"2025-04-04T03:14:55.697Z","avatar_url":"https://github.com/dragos-tudor.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# OpenIdConnect wip\n\n## ASPNET Security libraries\n- simplified ASPNET Security-like libraries.\n- rewritten from scratch ASPNET Security libraries.\n- \"light\" functional-style libraries [90% OOP-free].\n\n### Security libraries\n- [Authentication.Cookies](/Security.Authentication.Cookies/).\n- [Authentication.Facebook](/Security.Authentication.Facebook/).\n- [Authentication.Google](/Security.Authentication.Google/).\n- [Authentication.Twitter](/Security.Authentication.Twitter/).\n- [Authentication.BearerToken](/Security.Authentication.BearerToken/).\n- [Authentication.OAuth2](/Security.Authentication.OAuth/).\n- [Authentication.OpenIdConnect](/Security.Authentication.OpenIdConnect/).\n- [Authorization](/Security.Authorization/).\n- [DataProtection](/Security.DataProtection/).\n\n### Design\n- security services [authentication and authorization services] represent the **mechanism backbone**.\n- security services [*high-level* functions] act as **security behaviour controllers** and represent public API.\n- security libraries were written following some of **FP principles** [pure functions, high-order functions, immutability, data/behaviour separation, static methods/functions as first-class citiziens, result pattern].\n- DI is used as **thin layer** usually over functional security services [eg. *SignInCookie* have 2 implementations with/without DI services]. DI services implementations are registred as usual with specific method extensions [eg. *AddCookiesServices*, *AddFacebookServices*].\n- **security mechanism** is based on security services [authentication scheme free-mechanism]:\n  - *authentication middleware* receive authentication service as param [*UseAuthentication* extension].\n  - *authorization middleware* receive challenge and forbid services as params [*UseAuthorization* extension].\n  - *oauth callback endpoints* receive signin service as param [eg. *MapFacebook*].\n- authentication libraries implement specific authentication services [eg. *AuthenticateCookie*, *SignInCookie*, *ChallengeGoogle*, *AuthenticateFacebook*].\n- authorization library implement authorization services [eg. *Authorize*].\n- *high-level* functions usually use declarative style [eg. *SignInCookie*].\n  - usually impure functions [with side-effects].\n  - built on top of *low-level* and *intermediate-level* functions.\n- *intermediate-level* functions use imperative/declarative style [eg. *SetAuthorizationParams*].\n- *low-level* functions usually use imperative style and are one-liners [eg. *IsSecuredCookie*].\n  - usually pure [without side effects] or semi-pure functions [side effects on parameters].\n- *high-intermediate-low-level* hierarchy design I named it **lego principle**. It could be seen also as a **functions pyramid** having at the base *low-level* functions.\n- **no else** strategy [0 (zero) else branches].\n\n### Processes\n- there are 2 different security processes: *local authentication* and *remote authentication*.\n- *local authentication process* [cookie]:\n  - each request [when use authentication middlware] call *authentication func* [eg. *AuthenticateCookie*]. Based on authentication result the middleware set *HttpContext.User* prop.\n  - then each request [when use authorization middlware] call *authorization func* [eg. *Authorize*]. Based on authorization policies result is decided if the request is allowed, unauthenticated/challenged or unauthorized/forbidden.\n  - signin/signout funcs are used on specific endpoints/controller actions implememted by devs.\n- *remote authentication process* [OAuth2 protocol]:\n  - when called, the *challenge endpoint* [eg. registered with *MapFacebook*] build and send an authorization request to authorization server.\n  - after processing the authorization request the authorization server redirect response to *callback endpoint* [eg. registered with *MapFacebook*]. That endpoint receive authorization server response and call *callback func* [eg. *CallbackFacebook*, *CallbackOAuth*]. The *callback func* has 2 steps:\n    - authentication: *AuthenticateOAuth* oauth authentication func has 3 substeps:\n      * *PostAuthorization* - validate the authorization code and the request from the authorization server [local].\n      * *ExchangeCodeForTokens* - exchange with the authorization server the authorization code for the access [and refresh] tokens [remote].\n      * *AccessUserInfo* - using access token gets from the authorization server the user informations [remote].\n      * The authentication step transform user informations received from authorization server into security claims, add them to the claims identity, create an authentication ticket and return the *AuthenticationResult*.\n    - signin: after oauth authentication step when authentication succedded then the signin func is called [eg. *SiginInCookie^, *SignInBearerToken*]. Signin func is set on oauth endpoints registration.\n  - after callback redirection next requests will use *local authentication process*.\n\n### Remarks\n- *completely* rewritten authentication mechanism.\n- *partially* rewritten authorization mechamism [keeping compatibility with ASPNET authorization policies mechanism].\n- cookie authentication services *surgically* implement session-based cookies feature [using *IsSessionBasedCookie* func]. Authenticating, signingin and signingout services are completely independent each other [no dependencies on HttpContext features]. *AuthenticationSessionCookie*, *SignInSessionCookie* and *SignOutSessionCookie* session-based cookies services are completely isolated from non-session based versions.\n- authentication options implementation contains only data [eg. *CookieAuthenticationOptions*]. Cookie authentication services [non DI-based ones] receive all dependencies as parameters.\n- Microsoft ASPNET authentication options implementation contains data and behaviour/services [eg. *SessionStore*, *TicketDataFormat*, *SystemClock* for *CookieAuthenticationOptions*]. This design have some advantages comparing with my implementation allowing options:\n  - to have different services from those registered on DI.\n  - to encapsulate and carry on those services through the authentication process [reducing the number of parameters so].\n- *AuthenticateOAuth* oauth authentication func use template method design pattern allowing oauth libraries to override/decorate when neccessary *postAuthenticate*, *exchangeCodeForTokens* or *accessUserInfo* authentication substeps [eg. *AuthenticateTwitter*, *AuthenticateFacebook*].\n- redirecting remarks:\n  - *ChallengeOAuth* and *ChallengeOidc* funcs redirect to authorization server [*ChallengeOidc* could use form instead of redirection].\n  - *CallbackOAuth* and *CallbackOidc* funcs redirect to original url or when callback authentication error to *AccessDeniedPath* or *ErrorPath* authentication options props depending of error type.\n  - *SigninCookie*, *SignOutCookie*, *Challenge**, *Forbid** etc. no redirections [webapi oriented functionality]. when redirections are neccessary those funcs could be decorated and redirected to *AuthenticationProperties.RedirectUri* or to *AuthenticationOptions.ReturnUrlParameter* query parameter.\n- cookies remarks:\n  - *AuthenticationCookieOptions.ExpiresAfter* single place to control *AuthenticationTicket* [cookies] persistency.\n  - *AuthenticationCookieOptions.CookieName* single place to control cookies names.\n- oidc remarks:\n  - *pkce* is the recommended solution regarding security for *authorization code* flow.\n  - *implicit* and *hybrid* flows not supported based on oidc best practices [even supported by [oidc rfc](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest)].\n  - *nonce* is not neccessary because *implicit* and *hybrid* are only flows with required *nonce* parameter.\n\n### Project goals\n- to untangle/demystify the ASPNET authentication/authorization mechanisms and local/remote processes.\n- to simplify authentication/authorization mechanisms [ASPNET schema-based free mechanism].\n- to demonstrate a functional programming implementation.\n- to demonstrate a practical alternative to OOP.\n\n### Benchmark\n| Method    | InvocationCount | Mean      | Error     | StdDev    | Median    | Ratio | RatioSD | Gen0    | Gen1    | Gen2    | Allocated | Alloc Ratio |\n|---------- |---------------- |----------:|----------:|----------:|----------:|------:|--------:|--------:|--------:|--------:|----------:|------------:|\n| FPSignin  | 128             |  64.34 μs |  1.196 μs |  1.119 μs |  64.69 μs |  1.00 |    0.00 |       - |       - |       - |   7.96 KB |        1.00 |\n| OOPSignin | 128             |  79.98 μs |  3.247 μs |  9.212 μs |  79.56 μs |  1.13 |    0.14 |  7.8125 |  7.8125 |  7.8125 | 116.21 KB |       14.59 |\n|           |                 |           |           |           |           |       |         |         |         |         |           |             |\n| FPSignin  | 512             |  45.75 μs |  5.065 μs | 14.934 μs |  39.12 μs |  1.00 |    0.00 |  1.9531 |       - |       - |   7.96 KB |        1.00 |\n| OOPSignin | 512             |  97.08 μs |  7.432 μs | 21.679 μs |  95.52 μs |  2.41 |    1.12 |  9.7656 |  9.7656 |  9.7656 |  445.7 KB |       56.01 |\n|           |                 |           |           |           |           |       |         |         |         |         |           |             |\n| FPSignin  | 1024            |  28.83 μs |  2.009 μs |  5.533 μs |  26.26 μs |  1.00 |    0.00 |  1.9531 |       - |       - |   7.95 KB |        1.00 |\n| OOPSignin | 1024            | 186.15 μs | 26.776 μs | 78.949 μs | 211.04 μs |  6.32 |    3.02 | 14.6484 | 13.6719 | 13.6719 | 915.64 KB |      115.12 |\n- for InvocationCount \u003e 2048 OOP benchmark start running extremely slow.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdragos-tudor%2Fbackend-security","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdragos-tudor%2Fbackend-security","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdragos-tudor%2Fbackend-security/lists"}