Getting login credentials from data sources
The following sections describe how to look up credentials to log in to the sample app:
Log in with credentials from a file
The following figure illustrates the flow of requests when IG uses credentials in a file to log a user in to the sample app:
-
IG intercepts the browser’s HTTP GET request, which matches the route condition.
-
The PasswordReplayFilter confirms that a login page is required, and
-
The FileAttributesFilter uses the email address to look up the user credentials in a file, and stores the credentials in the request context attributes map.
-
The PasswordReplayFilter retrieves the credentials from the attributes map, builds the login form, and performs the HTTP POST request to the sample app.
-
The sample application validates the credentials, and responds with a profile page.
-
The ReverseProxyHandler passes the response to the browser.
Before you start, prepare IG and the sample application as described in the Getting started.
-
On your system, add the following data in a comma-separated value file:
-
Linux
-
Windows
/tmp/userfile.txt
C:\Temp\userfile.txt
username,password,fullname,email george,C0stanza,George Costanza,george@example.com kramer,N3wman12,Kramer,kramer@example.com bjensen,H1falutin,Babs Jensen,bjensen@example.com demo,Ch4ng31t,Demo User,demo@example.com kvaughan,B5ibery12,Kirsten Vaughan,kvaughan@example.com scarter,S9rain12,Sam Carter,scarter@example.com
-
-
Set up IG:
-
Add the following route to IG, to serve .css and other static resources for the sample application:
-
Linux
-
Windows
$HOME/.openig/config/routes/static-resources.json
%appdata%\OpenIG\config\routes\static-resources.json
{ "name" : "sampleapp-resources", "baseURI" : "http://app.example.com:8081", "condition": "${find(request.uri.path,'^/css')}", "handler": "ReverseProxyHandler" }
-
-
Add the following route to IG:
-
Linux
-
Windows
$HOME/.openig/config/routes/02-file.json
%appdata%\OpenIG\config\routes\02-file.json
{ "name": "02-file", "condition": "${find(request.uri.path, '^/profile')}", "capture": "all", "handler": { "type": "Chain", "baseURI": "http://app.example.com:8081", "config": { "filters": [ { "type": "PasswordReplayFilter", "config": { "loginPage": "${find(request.uri.path, '^/profile/george') and (request.method == 'GET')}", "credentials": { "type": "FileAttributesFilter", "config": { "file": "/tmp/userfile.txt", "key": "email", "value": "george@example.com", "target": "${attributes.credentials}" } }, "request": { "method": "POST", "uri": "http://app.example.com:8081/login", "form": { "username": [ "${attributes.credentials.username}" ], "password": [ "${attributes.credentials.password}" ] } } } } ], "handler": "ReverseProxyHandler" } } }
Notice the following features of the route:
-
The route matches requests to
/profile
. -
The
PasswordReplayFilter
specifies aloginPage
page property:-
When a request is an HTTP GET, and the request URI path is
/profile/george
, the expression resolves totrue
. The request is directed to a login page.The
FileAttributesFilter
looks up the key and value in/tmp/userfile.txt
, and stores them in the context.The
request
object retrieves the username and password from the context, and replaces the browser’s original HTTP GET request with an HTTP POST login request, containing the credentials to authenticate. -
For other requests, the expression resolves to
false
. The request passes to the ReverseProxyHandler, which directs it to the profile page of the sample app.
-
-
-
-
Test the setup:
-
Go to http://ig.example.com:8080/profile/george.
Because the property
loginPage
resolves totrue
, the PasswordReplayFilter processes the request to obtain the login credentials. The sample app returns the profile page for George. -
Go to http://ig.example.com:8080/profile/bob, or to any other URI starting with
http://ig.example.com:8080/profile
.Because the property
loginPage
resolves tofalse
, the PasswordReplayFilter passes the request directly to the ReverseProxyHandler. The sample app returns the login page.
-
Log in with credentials from a database
This section describes how to configure IG to get credentials from a database. This example is tested with Jetty and H2 1.4.197.
The following figure illustrates the flow of requests when IG uses credentials from a database to log a user in to the sample app:
-
IG intercepts the browser’s HTTP GET request.
-
The PasswordReplayFilter confirms that a login page is required, and passes the request to the SqlAttributesFilter.
-
The SqlAttributesFilter uses the email address to look up credentials in H2, and stores them in the request context attributes map.
-
The PasswordReplayFilter retrieves the credentials from the attributes map, builds the login form, and performs the HTTP POST request to the sample app.
-
The sample application validates the credentials, and responds with a profile page.
Before you start, prepare IG and the sample application as described in the Getting started.
-
Set up the database:
-
On your system, add the following data in a comma-separated value file:
-
Linux
-
Windows
/tmp/userfile.txt
C:\Temp\userfile.txt
username,password,fullname,email george,C0stanza,George Costanza,george@example.com kramer,N3wman12,Kramer,kramer@example.com bjensen,H1falutin,Babs Jensen,bjensen@example.com demo,Ch4ng31t,Demo User,demo@example.com kvaughan,B5ibery12,Kirsten Vaughan,kvaughan@example.com scarter,S9rain12,Sam Carter,scarter@example.com
-
-
Download and unpack the H2 database, and then start H2:
$ sh /path/to/h2/bin/h2.sh
H2 starts, listening on port 8082, and opens the H2 Console in a browser.
-
In the H2 Console, select the following options, and then select Connect to access the console:
-
Saved Settings :
Generic H2 (Server)
-
Setting Name :
Generic H2 (Server)
-
Driver Class:
org.h2.Driver
-
JDBC URL:
jdbc:h2:~/ig-credentials
-
User Name:
sa
-
Password :
password
If you have run this example before but can’t access the console now, try deleting your local ~/ig-credentials
files and starting H2 again.
-
-
In the console, add the following text, and then run it to create the user table:
DROP TABLE IF EXISTS USERS; CREATE TABLE USERS AS SELECT * FROM CSVREAD('/tmp/userfile.txt');
-
In the console, add the following text, and then run it to verify that the table contains the same users as the file:
SELECT * FROM users;
-
Add the .jar file
/path/to/h2/bin/h2-*.jar
to the IG configuration:-
For IG in standalone mode, create the directory
$HOME/.openig/extra
, where$HOME/.openig
is the instance directory, and add .jar files to the directory. -
For IG in web container mode, add .jar files to the web container classpath. For example, in Jetty use
/path/to/jetty/webapps/ROOT/WEB-INF/lib
.
-
-
-
Set up IG:
-
Set an environment variable for the database password, and then restart IG:
$ export DATABASE_PASSWORD='cGFzc3dvcmQ='
The password is retrieved by a SystemAndEnvSecretStore, and must be base64-encoded.
-
Add the following route to IG, to serve .css and other static resources for the sample application:
-
Linux
-
Windows
$HOME/.openig/config/routes/static-resources.json
%appdata%\OpenIG\config\routes\static-resources.json
{ "name" : "sampleapp-resources", "baseURI" : "http://app.example.com:8081", "condition": "${find(request.uri.path,'^/css')}", "handler": "ReverseProxyHandler" }
-
-
Add the following route to IG:
-
Linux
-
Windows
$HOME/.openig/config/routes/03-sql.json
%appdata%\OpenIG\config\routes\03-sql.json
{ "heap": [ { "name": "SystemAndEnvSecretStore-1", "type": "SystemAndEnvSecretStore" }, { "name": "JdbcDataSource-1", "type": "JdbcDataSource", "config": { "driverClassName": "org.h2.Driver", "jdbcUrl": "jdbc:h2:tcp://localhost/~/test", "username": "sa", "passwordSecretId": "database.password", "secretsProvider": "SystemAndEnvSecretStore-1" } } ], "name": "sql", "condition": "${find(request.uri.path, '^/profile')}", "handler": { "type": "Chain", "baseURI": "http://app.example.com:8081", "config": { "filters": [ { "type": "PasswordReplayFilter", "config": { "loginPage": "${find(request.uri.path, '^/profile/george') and (request.method == 'GET')}", "credentials": { "type": "SqlAttributesFilter", "config": { "dataSource": "JdbcDataSource-1", "preparedStatement": "SELECT username, password FROM users WHERE email = ?;", "parameters": [ "george@example.com" ], "target": "${attributes.sql}" } }, "request": { "method": "POST", "uri": "http://app.example.com:8081/login", "form": { "username": [ "${attributes.sql.USERNAME}" ], "password": [ "${attributes.sql.PASSWORD}" ] } } } } ], "handler": "ReverseProxyHandler" } } }
Notice the following features of the route:
-
The route matches requests to
/profile
. -
The PasswordReplayFilter specifies a loginPage page property:
-
When a request is an HTTP GET, and the request URI path is
/profile/george
, the expression resolves to true. The request is directed to a login page.The
SqlAttributesFilter
specifies the data source to access, a prepared statement to look up the user’s record, a parameter to pass into the statement, and where to store the search results in the request context attributes map.The
request
object retrieves the username and password from the context, and replaces the browser’s original HTTP GET request with an HTTP POST login request, containing the credentials to authenticate.The request is for
username, password
, but H2 returns the fields asUSERNAME
andPASSWORD
. The configuration reflects this difference. -
For other requests, the expression resolves to
false
. The request passes to the ReverseProxyHandler, which directs it to the profile page of the sample app.
-
-
-
-
Test the setup:
-
Go to http://ig.example.com:8080/profile.
Because the property
loginPage
resolves tofalse
, the PasswordReplayFilter passes the request directly to the ReverseProxyHandler. The sample app returns the login page. -
Go to http://ig.example.com:8080/profile/george.
Because the property
loginPage
resolves totrue
, the PasswordReplayFilter processes the request to obtain the login credentials. The sample app returns the profile page for George.
-