NGINX as an OpenID Connect RP with WSO2 Identity Server — Part 1
Using OpenResty Web Server
NGINX… an all-in-one light and fast webserver, highly optimized for web serving, reverse proxying, caching, load balancing, media streaming, and much more. However this post is not going to be about how to configure NGINX as a load balancer or anything. This is a different use case. Here, I will show how to configure NGINX as an OpenID Connect Relying Party with the help of an open source library designed for NGINX — lua-resty-openidc.
lua-resty-openidc is a library for NGINX implementing the OpenID Connect Relying Party (RP) and the OAuth 2.0 Resource Server (RS) functionality. Following are its most significant features.
- When used as an OpenID Connect Relying Party it authenticates users against an OpenID Connect Provider using OpenID Connect Discovery and the Basic Client Profile (i.e. the Authorization Code flow). Upon the user giving consent, the it will gather user information from the OpenID Provider and if the user is authorized, forward the user to a redirect URI.
- When used as an OAuth 2.0 Resource Server it can validate OAuth 2.0 Bearer Access Tokens against an Authorization Server or, in case a JSON Web Token is used for an Access Token, verification can happen against a pre-configured secret/key .
- It maintains sessions for authenticated users by leveraging lua-resty-session thus offering a configurable choice between storing the session state in a client-side browser cookie or use in of the server-side storage mechanisms shared-memory/memcache/redis.
- It supports server-wide caching of resolved Discovery documents and validated Access Tokens.
- It can be used as a reverse proxy terminating OAuth/OpenID Connect in front of an origin server so that the origin server/services can be protected with the relevant standards without implementing those on the server itself.
What is the benefit of configuring NGINX as an OIDC RP?
The advantage is that you can secure your applications with OAuth without any application-side changes. All your application servers sitting behind NGINX will know nothing about user authentication. NGINX will do the authentication on behalf of the applications and send the authorized user claims as custom headers to the back-end.
Part 1 of this series elaborates the easiest and most straight-forward method to accomplish our requirement. We are going to make use of OpenResty Server for this.
OpenResty is a web server which extends NGINX by bundling it with many useful Nginx modules and Lua libraries. This includes lua-resty-openidc library as well. OpenResty excels at scaling web applications and services. For example, one module it includes enables you to write Lua code which will execute directly in an Nginx worker, enabling high-performance applications. So if you have OpenResty server you don’t need to install NGINX separately. It’s included in OpenResty and having it installed ahead of time will definitely conflict.
As the OpenID Provider (Authorization Server) we will be using WSO2 Identity Server 5.6.0 which is the latest released version at the time of writing this article. So go ahead and download Identity Server from the product download page.
Installing OpenResty Server
For configuring Open Resty Server (NGINX as RP) we need the following additional dependencies.
— gcc
— libpcre3 libpcre3-dev
— libssl-dev
— lua5.1
— lua-resty-http
— lua-resty-session
— lua-resty-jwt
Let’s first install the required dependencies.
sudo apt update
sudo apt-get install gcc libssl-dev libpcre3 libpcre3-dev make
Now we’ll install OpenResty version 1.11.2.5
wget https://openresty.org/download/openresty-1.11.2.5.tar.gz
tar -xvf openresty-1.11.2.5.tar.gz
cd openresty-1.11.2.5
./configure -j2
make -j2
sudo make install
After that, we can download the lua-resty dependencies with OPM. opm
is the official OpenResty package manager, similar to NodeJS's npm.
opm install bungle/lua-resty-session
opm install pintsized/lua-resty-http
opm install zmartzone/lua-resty-openidc
NOTE: opm assumes the directory layout of OpenResty. For custom setup, you’ll have follow either of the following options.
- copy the OpenResty directory structure.
- add the paths of your resty, nginx, and luajit executables to your PATH environment.
Configuring WSO2 Identity Server
Go to <IS_SERVER_HOME>/repository/conf/identity/identity.xml and find <IDTokenIssuerID> property. This can be found under <OpenIDConnect> element. Change the value of this property to following. Replace <IS_HOST> and <IS_PORT> with corresponding values.
https://<IS_HOST>:<IS_PORT>/oauth2/oidcdiscovery/.well-known/openid-configuration
lua-resty-openidc relies on the well-known openId connect discovery API of an OpenID Provider to learn OIDC metadata about the Authorization Server. In WSO2 Identity Server, by default this API is secured with Basic Authentication. In order to call /.well-known openId connect discovery API from our NGINX server, we need to remove this security from identity.xml. Since this API is used to provide the OpenID Connect metadata information of the WSO2 Identity Server, there won’t be an issue in exposing the API to public.
Go to <IS_SERVER_HOME>/repository/conf/identity/identity.xml and set secured=”false” to the following property which can be found under <ResourceAccessControl> element.
<Resource context=”(.*)/.well-known(.*)” secured=”true” http-method=”all”/>
Now start Identity Server with wso2server.sh.
Login to management console (https://<IS_HOST>:<IS_PORT>/carbon) with admin:admin credentials.
Go to Resident Identity provider > Inbound Authentication Configuration > OAuth2/OpenID Connect Configuration
Update the ‘Identity Provider Entity Id’ as https://<IS_HOST>:<IS_PORT>/oauth2/oidcdiscovery/.well-known/openid-configuration
Now we need to create a service provider for NGINX..
Go to service providers > add new service provider
Give a name to the service provider — e.g. lua-resty-openidc
Expand Inbound Authentication Configuration of the created service provider and select OAuth/OpenID Connect Configuration. Click configure.
In the OAuth/OIDC config page you only need to give the call back url. Keep the other configurations as it is.
Call back url : https://localhost/welcome
Click update.
Now if you click OAuth/OpenID Connect Configuration again under Inbound Authentication Configuration, you can see that Oauth Client Key and Client Secret has been generated for our application. We need to use these values when configuring NGINX conf file. Let’s do that next.
Configuring OpenResty’s NGINX
First let’s create some SSL certificates and then use those in our Nginx configuration.
sudo mkdir -p /usr/local/openresty/nginx/ssl/
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /usr/local/openresty/nginx/ssl/nginx.key -out /usr/local/openresty/nginx/ssl/nginx.crt
Fill out the prompts for your certificates. We’ll use them in the following NGINX configuration.
OpenResty’s nginx.conf can be found at /usr/local/openresty/nginx/conf/nginx.conf
. Replace $CLIENT_ID and $CLIENT_SECRET with the actual client ID and client secret generated from the OpenID Connect service provider we just created in Identity Server. Replace $IS_HOST with the Identity Server host name and $IS_PORT with the port IS is running on.
events {
worker_connections 1024;
}http { lua_package_path "/usr/local/openresty/?.lua;;"; resolver 127.0.0.1; lua_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;
lua_ssl_verify_depth 5; # cache for discovery metadata documents
lua_shared_dict discovery 1m;
# cache for JWKs
lua_shared_dict jwks 1m; server {
listen 80 default_server;
server_name _;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl; ssl_certificate /usr/local/openresty/nginx/ssl/nginx.crt;
ssl_certificate_key /usr/local/openresty/nginx/ssl/nginx.key; location / { access_by_lua_block { local opts = {
redirect_uri_path = "/welcome",
discovery = "https://$IS_HOST:$IS_PORT/oauth2/oidcdiscovery/.well-known/openid-configuration",
client_id = "$CLIENT_ID",
client_secret = "$CLIENT_SECRET",
ssl_verify = "no",
scope = "openid email profile",
redirect_uri_scheme = "https",
} -- call authenticate for OpenID Connect user authentication
local res, err = require("resty.openidc").authenticate(opts) if err then
ngx.status = 500
ngx.say(err)
ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
end ngx.req.set_header("X-USER", res.id_token.sub)
}
}
}
}
Tip: you can find the resolver (domain name server) in /etc/resolv.conf
file.
Running OpenResty Server
For accessing files in /usr/local directory we need a shell running as root, as though it were produced from a root login with all the environment variables set for root rather than for your default user.
Let’s create a new root shell session with following command.
sudo -i
Then add the OpenResty bin to PATH:
export PATH=/usr/local/openresty/bin:$PATH
Save this configuration file. Let’s run the openresty
command we added to our path in the same shell.
openresty
Now navigate to the RP in your browser. Since we have configured default SSL port for NGINX (443) we can access NGINX from browser without specifying the port.
https://localhost
This will redirect you to WSO2 Identity Server Login Page.
Log in with valid user credentials and authorize the RP
If everything was successful you will be re-directed back to the OpenResty welcome page.
In Part 2 of this series, I will show how to configure lua-resty-openidc on top of an exiting NGINX installation WITHOUT using OpenResty. However what is recommended is to use OpenResty if you need OIDC facilities with NGINX. But the latter is also possible even-though it does have some difficulties with installation process.
Thanks for reading my lengthy post! Have a nice day! :)
Common errors you might come across during configuration..
nginx: [emerg] bind() to [::]:80 failed (98: Address already in use)
In my case this issue occurred since apache was running in the background and prevented NGINX from starting on the desired port. Run following command to stop apache server.
sudo /etc/init.d/apache2 stop
ERROR: luajit is required but is not available according to resty:
If you came accross this issue, you are missing the resty
command-line utility in your PATH
environment, which is shipped with OpenResty. The common case might be that the user you are running (e.g. sudo) may have a different PATH environment than your default shell session. Try resty -e 'print("hello")'
command to check if resty is properly set to the PATH environment.
resty:Can not find command <-- if you faced this, then you need to export resty to your user's PATH environment with following command.export PATH=/usr/local/openresty/bin:$PATH