{"id":15620870,"url":"https://github.com/ig3/nginx-auth-request-azuread","last_synced_at":"2025-07-30T14:38:27.871Z","repository":{"id":57682484,"uuid":"470328285","full_name":"ig3/nginx-auth-request-azuread","owner":"ig3","description":"nginx auth_request to Azure AD integration for authentication and authorization. ","archived":false,"fork":false,"pushed_at":"2024-07-16T16:35:49.000Z","size":531,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-07T02:49:17.955Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ig3.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-03-15T20:53:11.000Z","updated_at":"2025-03-26T21:41:08.000Z","dependencies_parsed_at":"2024-07-16T20:11:50.375Z","dependency_job_id":"42495498-14cf-492a-b309-47055b1e292d","html_url":"https://github.com/ig3/nginx-auth-request-azuread","commit_stats":{"total_commits":54,"total_committers":2,"mean_commits":27.0,"dds":"0.42592592592592593","last_synced_commit":"9cab72c9ca9eed77b60624966dac0eda9bdb15eb"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ig3/nginx-auth-request-azuread","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ig3%2Fnginx-auth-request-azuread","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ig3%2Fnginx-auth-request-azuread/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ig3%2Fnginx-auth-request-azuread/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ig3%2Fnginx-auth-request-azuread/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ig3","download_url":"https://codeload.github.com/ig3/nginx-auth-request-azuread/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ig3%2Fnginx-auth-request-azuread/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265809975,"owners_count":23831944,"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":[],"created_at":"2024-10-03T09:03:08.212Z","updated_at":"2025-07-18T18:31:53.005Z","avatar_url":"https://github.com/ig3.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# nginx-auth-request-azuread (naraad)\n\nThis is an HTTP server that provides authentication for a website served by\nnginx, based on Azure AD OAuth 2.0 service.\n\n## Installation\n\n```\n$ npm install -g @ig3/nginx-auth-request-azuread\n```\n\nOR\n\n```\n$ git clone https://github.com/ig3/nginx-auth-request-azuread.git\n$ npm install\n$ npm pack\n$ npm install -g ig3-nginx-auth-request-azuread-*.tgz\n```\n\nNote: `npm pack` creates an installation package and installing this does\nan installation similar to installing from the registry, with all the\ncontents copied to the installation directory, as opposed to `npm install\n-g` which will link to the current directory rather than making a copy of\nthe contents. The latter might be preferable for testing and development,\nbut not for production.\n\n## Configuration\n\nCoordinated configuration is required for Azure AD, nginx and naraad (this\nwebsite).\n\n\n### Azure AD\n\nAdd an application under `App registrations`.\n\nUnder `Authentication`, `Platform configurations` add web application\n`Web`. Enter the redirect URI. If you use naraad to protect multiple\nwebsites, you can add multiple redirect URIs: one for each site. There is\nno provision for signout, so no need to set a logout URL.\n\nUnder `Certificates \u0026 secrets`, add a Client Secret. Save the value, to be\nadded to naraad configuration (see below). You cannot view the secret value\nlater, but you can always add a new secret.\n\nUnder `Token configuration` add claims `email`, `family_name`,\n`given_name`, `upn` and `verified_primary_email`. \n\nUnder `API permissions` add `openid`\n\n### nginx\n\nThe naraad server responds to three paths: /verify, /authenticate and\n/callback. The nginx server must be configured to pass requests to these\nthree routes.\n\nEvery request for a protected resource must be passed to the /verify route.\nThe naraad server will respond with status 200 if the user is authenticated\nor 401 if not.\n\nThe 401 response must be routed to the /authenticate route to initiate\nauthenticate. The naraad server will respond with a redirect to the OAuth\nauthentication endpoint. This must set two headers: X-Original-URL and\nX-Callback-URL\n\nThe callback from OAuth must be passed to the /callback route. The naraad\nserver will respond with a redirect to the originally requested URL.\n\nThe nginx\n[`auth_request`](https://nginx.org/en/docs/http/ngx_http_auth_request_module.html#auth_request)\ndirective must be added to any protected content\n(i.e. content for which authentication and authorization are required),\nwith additional configuration to handle the subrequest from `auth_request`\nand the redirect from OAuth 2.0.\n\nExample configuration:\n\n```\nserver {\n  server_name www.example.com;\n  listen 443 ssl;\n\n  location / {\n    auth_request /auth/verify;\n    auth_request_set $auth_cookie $upstream_http_set_cookie;\n    add_header Set-Cookie $auth_cookie;\n\n    root /var/www/htdocs;\n  }\n\n  location = /auth/verify {\n    proxy_pass http://127.0.0.1:9090/verify;\n    proxy_pass_request_body off;\n    proxy_set_header Content-Length \"\";\n  }\n\n  error_page 401 = /auth/authenticate;\n\n  location /auth/authenticate {\n    proxy_pass http://127.0.0.1:9090/authenticate;\n    proxy_set_header X-Original-URL $request_uri;\n    proxy_set_header X-Auth-Root $scheme://$host/auth;\n    proxy_set_header X-App myapp;\n  }\n\n  location /auth/ {\n    proxy_pass http://127.0.0.1:9090/;\n    proxy_set_header X-Auth-Root $scheme://$host/auth;\n    proxy_set_header X-App myapp;\n  }\n}\n```\n\nThe paths for the website and for the naraad server are\nindependent of each other. In the website, any path that does not conflict\nwith other paths in the website may be used, instead of `/auth/verify`,\n`/auth/authenticate` and `/auth/callback` in this example configuration.\n\nThe path for the callback from OAuth must correspond to a Redirect URI\nconfigured in the Azure AD application.\n\nOne the proxy to naraad `/authenticate`, the headers X-Oringial-URL and\nX-Callback-URL must be set to the URL the user originally requested and the\nURL of the OAuth callback / redirect respectively. If X-Original-URL is not\nset, on completion of authentication the user will be redirected to the\nroot of the website (path '/'), regardless of what path was originally\nrequested. If X-Callback-URL is not set, the authentication will fail:\nOAuth requires that the callback URL be specified.\n\nNote that a single instance of naraad can provide authentication for\nmultiple web sites, each with their own callback / redirect. The callback /\nredirect for a specific web site is specified by the Header X-Callback-URL\non the request to /authenticate.\n\nHeader `X-Auth-Root` provides the prefix of URL to which various paths are\nappended for authentication, callbacks, etc. \n\nHeader `X-App` selects the application configuration used to compose the\ntoken returned by authentication.\n\nHeaders `X-Auth-Root` and `X-App` should be set on every request proxied to\nthis server, except the `/verify` path, where they are ignored.\n\n### naraad\n\nThere are a few configuration parameters for the naraad server. These\nmostly relate to OAuth and must be coordinated with the application\nconfigured in Azure AD.\n\nConfiguration may be JSON5, JSON or INI style, according to the extension\nof the configuration file.\n\nConfiguration file paths:\n * /etc/naraad\n * /etc/naraad/config\n * ~/.config/naraad\n * ~/.config/naraad/config\n * ~/.naraad\n * ~/.naraad/config\n * .naraad\n * naraad\n\nEach of these paths will be searched, with extensions .json5, .json and\n.ini and any configuration file found will be loaded. If multiple files are\nfound they will all be loaded and their contents merged with files loaded\nlater overriding those loaded earlier, if they contain configuration\nparameters with the same name.\n\nEnvironment variables with names beginning with  `naraad_` will be added to the\nconfiguration, possibly overriding parameters from the configuration files.\nThe leading `naraad_` will be removed from the environment variable name. For\nexample, environment variable `naraad_server_address` would set configuration\nparameter `server_address`.\n\nConfiguration parameter may also be specified on the command line. For\nexample: `naraad --server_address 1.2.3.4` would set the server IP address to\n1.2.3.4. Configuration parameters set from the command line will override\nany settings from configuration files or environment variables.\n\nExample configuration:\n\n```\n{\n  \"debug\": true,\n  \"server_port\": 9000,\n  \"providers\": {\n    \"o365\": {\n      \"name\": \"O365\",\n      \"type\": \"o365\",\n      \"oauth_client_id\": \"6ab28693-6728-4cba-9bf5-cb7fabd98659\",\n      \"oauth_client_secret\": \"***top secret***\",\n      \"oauth_scope\": \"User.Read openid\",\n      \"oauth_callback_url\": \"https://example.com/auth/office365/callback\",\n      \"oauth_server\": \"login.microsoftonline.com\",\n      \"oauth_authorization_url\": \"https://login.microsoftonline.com/da6bbd29-108b-43e2-9b37-06b2758737e9/oauth2/v2.0/authorize\",\n      \"oauth_authorization_path\": \"/da6bbd29-108b-43e2-9b37-06b2758737e9/oauth2/v2.0/authorize\",\n      \"oauth_token_url\": \"https://login.microsoftonline.com/da6ffd29-108b-43e2-9b37-0692c58b32e9/oauth2/v2.0/token\",\n      \"oauth_token_path\": \"/da6bbd29-108b-43e2-9b37-06b2758737e9/oauth2/v2.0/token\",\n      \"oauth_tenant\": \"example.com\"\n    }\n  },\n  \"applications\": {\n    \"eqa\": {\n      \"couchdb\": true,\n      \"groupMap\": {\n        \"EQA User\": \"eqa_user\",\n        \"EQA Admin\": \"eqa_admin\"\n      },\n      \"jwtSecret\": \"hello\",\n      \"jwtExpiry\": \"1h\"\n    },\n    \"withGroups\": {\n      \"groupMap\": {\n        \"All Users\": \"users\",\n        \"Domain Admins\": \"admins\"\n      }\n    }\n    \"adminOnly\": {\n      \"requireGroups\": \"Domain Admins\"\n    },\n    \"adminOrUser\": {\n      \"requireGroups\": [ \"Domain Admins\", \"Domain Users\" ]\n    }\n  }\n}\n```\n\n### Configuration Parameters\n\n#### debug\n\nIf debug is true, additional logs will be generated.\n\n#### jwtExpiry\n\ndefault: `1h`\n\nThe expiry time of the JWT token that is generated for an authenticated\nuser. \n\n#### server_port\n\ndefault: 9090\n\nThe TCP port on which the naraad server listens.\n\n#### server_address\n\ndefault: 0.0.0.0\n\nThe IP address on which the naraad server listens.\n\n#### providers\n\nA set of identity providers.\n\nThe key will be used in URLs, unescaped.\n\nEach provider must have:\n\n * name - the name of the provider presented to users\n * type - the type of provider: one of ('o365')\n * type specific parameters\n\nAt the moment, there is only one provider type supported: o365. It has the\nfollowing parameters:\n\n##### oauth_client_id\n\nThis is the OAuth 2.0 client ID. It must be consistent with the Client ID\nconfigured in the Azure AD application.\n\n##### oauth_client_secret\n\nThis is the secret that verifies the request to the OAuth 2.0 authentication\nserver. It must be consistent with one of the secrets configured in the\nAzure AD application.\n\n##### oauth_callback_url\n\nThis is the callback URL that will be included in the OAuth 2.0\nauthentication request. The Azure AD application must be configured to\naccept this callback URL and the nginx server must be configured to proxy\nthe callback to the `/callback` path of the naraad server.\n\n##### oauth_authorization_url\n\nThis is the URL that the user's browser will be directed to, to initiate the\nOAuth 2.0 authentication. It must be set to the OAuth 2.0 authentication\nendpoint of the Azure AD instance in which the application is configured.\n\n##### oauth_token_rul\n\nThis is the URL that the naraad server will access to obtain an access token.\n\n##### oauth_tenant\n\nThis is the Azure AD tenant: typically a domain name that is the root of the\nAzure AD domain.\n\n#### applications\n\nThe keys to application should match the value of the X-App header on the\nauthentication request.\n\n##### couchdb\n\nThis application type is specific to CouchDB backend. It adds properties\n`sub`, `name` and `_couchdb.roles` to the generated user object, and uses\nthe jwtSecret and jwtExpiry properties of the application object to encode\nthe generated JWT token. The jwtSecret should be as configured on the\nCouchDB server: it is the shared secret by which CouchDB trusts the JWT.\n\n##### groupMap\n\nMaps the displayName of groups in Azure AD to group names meaningful to the\napplication, and adds these to the user object if the user is a member of\nthe corresponding Azure AD group.\n\nFor each mapped group that the user is a member of, the user object will\ncontain a property in the user.groups object with properties:\n\n * description\n * displayName\n * groupTypes\n * id\n * mail\n * mailEnabled\n * securityEnabled\n\nThese are the values of the corresponding properties of the Azure AD group.\n\n##### requireGroups\n\nThe value of this property is the display name of an Azure AD group or an\narray of such display names. Access is allowed if the user is a member of\nany of the groups.\n\nIf the user is not a member of any of the groups, authentication will fail\nand the user will be redirected back to the login page.\n\n### systemd\n\nTo start the server from systemd, create a service file similar to:\n\n```\n[Unit]\nDescription=HTTP server for nginx auth_request authentication\n\n[Service]\nType=simple\nRestart=on-failure\nWorkingDirectory=/tmp\nStandardOutput=syslog\nStandardError=syslog\nSyslogIdentifier=naraad\nExecStart=/usr/local/bin/naraad\n\n[Install]\nWantedBy=multi-user.target\n```\n\n## OAuth 2.0 notes\n\nThis uses the\n[Microsoft identity platform and OAuth 2.0 authorization code flow](https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow).\n\nThe documentation of the flow indicates that the resource server ('Web API'\nin their diagram) must validate the access token but no guidance is provided\nas to how to do this.\n\nIn fact, the page says:\n\n\u003e Don't attempt to validate or read tokens for any API you don't own, including the tokens in this example, in your code. Tokens for Microsoft services can use a special format that will not validate as a JWT, and may also be encrypted for consumer (Microsoft account) users. While reading tokens is a useful debugging and learning tool, do not take dependencies on this in your code or assume specifics about tokens that aren't for an API you control.\n\nThis seems to contradict the indication in the diagram at the top of the\npage that the 'Web API' server should validate the token. Or is it that the\naccess token for the 'Web API' is a token for the API of that server?\n\nMicrosoft provides [sample\ncode](https://docs.microsoft.com/en-us/azure/active-directory/develop/sample-v2-code),\nincluding code for NodeJS/Express. What I have looked at indicates that it\nis 'beta' code.\n\nSee\n[Microsoft identity platform access tokens](https://docs.microsoft.com/en-us/azure/active-directory/develop/access-tokens) for some details of the access tokens issued by Azure AD.\n\n[# of days since the last alg=none JWT vulnerability](https://news.ycombinator.com/item?id=24347519) has some interesting comments about using the payload of JWTs without verifying them. I don't entirely agree: one uses data from trusted sources received by secure channels all the time. Do you verify data from your database every time you query it? No, because you trust the source and the channel. Likewise, if you get a token directly from the token server by HTTPS, it is quite reliable data. If, on the other hand, you get the token from a client who submitted an HTTP request to your public server, then certainly it should be verified before it is trusted as being any more reliable than any other of their input.\n\n## Azure AD notes\n\n### Group Membership\n\nAzure AD OAuth 2.0 interface provides limited information about group\nmembership. The only claim supported is the `groups` claim and this only\nprovides the set of group IDs but nothing more. In particular, no names are\nprovided. To get more information it is necessarly to look up the group\ndetails via some other API: LDAP or Graph are options.\n\nWhile the implicit flow is simple, it returns the token in a URL which has\nlimited length. Azure AD omits the groups claim if it determines that\nincluding the set of groups would make the resulting URL too long. See\n[AAD groups claim missing in JWT token for some users](https://stackoverflow.com/questions/45751985/aad-groups-claim-missing-in-jwt-token-for-some-users), which includes these quotes from Microsoft:\n\n\u003e In the implicit flow, oauth will return the Jwt directly from the intial /authorize call through a query string param. The http spec limits the length of a query string / url, so if AAD detects that the resulting URI would be exceeding this length, they replace the groups with the hasGroups claim.\n\n\u003e This is by design when using implicit grant flow, regardless the \"groupMembershipClaims\" setting in the manifest. It's to avoid to go over the URL length limit of the browser as the token is returned as a URI fragment. So, more or less after 4 user's groups membership, you'll get \"hasgroups:true\" in the token. What you can do is to make a separate call to the Graph API to query for the user's group membership.\n\nSee\n[Security tokens](https://docs.microsoft.com/en-us/azure/active-directory/develop/security-tokens) for details.\n\nSo, to get useful information about group membership, the OAuth 2.0 API is\ninsufficient. The current API appears to be\n[Microsoft Graph API](https://docs.microsoft.com/en-us/azure/active-directory/develop/microsoft-graph-intro).\n\nBut the Microsoft Graph API is obtuse and the documentation moreso. It is\nnot at all obvious how to get the names of the groups a user is a member of.\n\nSee\n[How to get group names of the user is a member of using Microsoft Graph API?](https://stackoverflow.com/questions/63502897/how-to-get-group-names-of-the-user-is-a-member-of-using-microsoft-graph-api). \n\n[Include AAD group name in the JWT token](https://stackoverflow.com/questions/59873599/include-aad-group-name-in-the-jwt-token)\n\n[Get list of Microsoft Azure AD group names using MSAL library in Angular 8](https://stackoverflow.com/questions/65694758/get-list-of-microsoft-azure-ad-group-names-using-msal-library-in-angular-8) shows a MS Graph query to get all group ID and name.\n\nI think what I need is\n[List user transitive memberOf](https://docs.microsoft.com/en-us/graph/api/user-list-transitivememberof?view=graph-rest-1.0\u0026tabs=http). This should provide all groups that a user is a member of, directory or indirectly.\n\nExplorting\n[graph-explorer](https://developer.microsoft.com/en-us/graph/graph-explorer)\nit seems the query `https://graph.microsoft.com/v1.0/me/transitiveMemberOf`\nreturns details of groups for the authenticated user. Now all I have to do\nis figure out how to call this given the access token from OAuth 2.0.\n\nBut among examples in the explorer there is `all groups I belong to\n(director or indirect membership) with count`\n\n`https://graph.microsoft.com/v1.0/me/transitiveMemberOf/microsoft.graph.group?$count=true`\n\nI suspect the `/microsoft.graph.group?$count=true` adds the count.\n\nBut the value without this is an array and it is trivial to get the size of\nthe array so one wonders what Microsoft things the value of the count field\nis.\n\nTo access the Graph API see\n[Get access on behalf of a user](https://docs.microsoft.com/en-us/graph/auth-v2-user)\n\nBased on this, it seems sufficient to get the access token from OAuth 2.0\nAPI then include this in an 'Authorization: Bearer \u003ctoken\u003e` header in the\nrequest to the Graph API.\n\n## Changes\n\n### 0.0.5 - 20220331\n\nUse URLSearchParams instead of querystring\n\n### 0.0.6 - 20220331\n\nUpdate version of @ig3/config\n\n### 0.0.7 - 20220408\n\nAdd support for applications: couchdb\n\n### 0.0.8 - 20220411\n\nAdd app flag requireGroups\n\n### 0.0.9 - 20220411\n\nStop using cookies for sensitive parameters.\n\n### 0.0.10 - 20220411\n\nFix reference to authRoot.\n\n### 1.0.0 - 20220429\n\nEnhance logging for application requireGroups\n\n### 1.0.3 - 20230419\n\nConsolidate various changes that have been in test for several months.\n\nAdd configuration to support multiple authentication providers, though\nOffice 365 is still the only one for which there is implementation.\n\n### 1.0.4 - 20240717\n * Replace tape with @ig3/test\n * Update dependencies\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fig3%2Fnginx-auth-request-azuread","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fig3%2Fnginx-auth-request-azuread","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fig3%2Fnginx-auth-request-azuread/lists"}