AbsaOSS Common Login service using JWT Public key signatures
To interact with the service, most notable endpoints are
/token/generate
to generate access & refresh tokens/token/refresh
to obtain a new access token with a still-valid refresh token/token/public-key
to obtain public key to verify tokens including their validity window
Please, refer to the API documentation below for details of the endpoints.
Once you request your token at /token/generate
endpoint, you will receive both an access token and a refresh token
{
"token": "...",
"refresh": "..."
}
Both tokens are signed by LS public key and carry the username (sub
), type
(access
/refresh
) and creation/expiry info (iat
/exp
).
During the time the refresh token is valid, you may refresh the access token (expired or not) using the /token/refresh
endpoint - as the service does not facilitate any internal service access to LDAP, both tokens must be sent.
On the side of the integrator, in order to trust the access token, one should do the following actions:
- obtain the public-key from LS at
/token/public-key
- verify that the access token
- is valid against this public-key (e.g. using
jwtt
library or similar) - is not expired
- has
type=access
- is valid against this public-key (e.g. using
Swagger doc site is available at http://localhost:port/swagger-ui.html
(substitute http://localhost:port
with any possible host and port you have deployed your package to.)
It is available for download while running the service at http://localhost:port/v3/api-docs.yaml
-
gets generated from code (specifically from Spring annotations)
The project requires a valid configuration file to run. An example configuration file is provided to take inspiration from.
The project will look for the Spring config in multiple places and in a specific order precisely as described at Spring boot - Externalized Configuration. Without having to study the whole documentation section, let us offer a few simple ways:
If you are looking to build and run locally, just supply the following argument:
--spring.config.location=api/src/main/resources/example.application.yaml
or set the following Environment Variable:
"SPRING_CONFIG_LOCATION=api/src/main/resources/example.application.yaml"
and you will be up and running. This will run the application using the provided example config for usage in local tests/development.
Externally-defined-application.yaml
option that will not package (i.e. pollute) the resulting package.
Shown in IDEA:
The implementation is heavily inspired by Enceladus that already contained a similar single purpose login functionality with JWT. Also, Tomcat@SBT implementation is on the other hand drawn from atum-service.
Currently, only a skeleton of the project exists. The project uses xsbt-web-plugin
plugin, therefore to get
the service running (also builds the service war
), one can run:
sbt
service / Tomcat / start
The Login Service allows users to select which authentication providers they would like to use as well as the order in which the authentication methods get prioritized.
This is done by setting a number (Greater than 0) on the order
tag for each auth method.
the number used indicates the order in which you would like to use the method.
For Example: The 2 methods currently enabled are config specified users:
loginsvc.rest.auth.provider.users.order = 1
and ldap:
loginsvc.rest.auth.provider.ldap.order = 2
In the above example, both methods are enabled with the config specified users taking priority over Ldap.
In order to disable an authentication protocol, set the order
property to 0
or just exclude the properties from the config for that auth provider.
Please ensure at least one auth method is enabled.
For the service account used to search Ldap, the Service Account Name and password may be specified in the config file. For a more secure approach, the service account name and password may be specified in AWS Secrets Manager and the application will fetch them from there.
The config also allows the user to specify additional claims to be added to the JWT token. These can be sourced from ldap or specified directly in the config depending on the auth provider used.
Format of attributes list under LDAP in config is:
ldap:
# Auth Protocol
# Set the order of the protocol starting from 1
# Set to 0 to disable or simply exclude the ldap tag from config
# NOTE: At least 1 auth protocol needs to be enabled
order: 2
domain: "some.domain.com"
url: "ldaps://some.domain.com:636/"
search-filter: "(samaccountname={1})"
service-account:
account-pattern: "CN=%s,OU=Users,OU=CORP Accounts,DC=corp,DC=dsarena,DC=com"
in-config-account:
username: "svc-ldap"
password: "password"
attributes:
<ldapFieldName>: "<claimName>"
ldapFieldName
is the name of the field in the LDAP server and claimName
is the name of the claim that will be added to the JWT token.
Uses LDAP(s) to authenticate user in Active Directory and to fetch groups that this user belongs to.
Requires ActiveDirectoryLDAPConfig(domain: String, url: String, searchFilter: String)
.
- Run
openssl s_client -connect <ldaps_host>:<ldaps_port>
. - Copy the part starting with
-----BEGIN CERTIFICATE-----
and ending with-----END CERTIFICATE-----
. - Create file
ldapcert.pem
and paste content from (2) there. - Import the certificate to
cacarts
:- For JDK8: Run
keytool -import -file ldapcert.pem -alias ldaps -keystore <path_to_jdk>/jre/lib/security/cacerts -storepass <password>
(default password is changeit). - For JDK11: Run
keytool -import -file ldapcert.pem -alias ldaps -keystore <path_to_jdk>/lib/security/cacerts -storepass <password>
(default password is changeit).
- For JDK8: Run
- Enter
yes
when prompted.
The Application allows for the user to allow the application to generate a key in memory. This is useful for single deployments and testing, however, may present issues when trying to deploy multiple login-services for redundancy. To get around this, the application allows for you to generate your keys in AWS Secrets manager and the application will periodically fetch them.
In order to setup for in-memory key generation, your config should look like so:
loginsvc:
rest:
jwt:
generate-in-memory:
access-exp-time: 15min
refresh-exp-time: 9h
key-rotation-time: 9h
alg-name: "RS256"
There are a few important configuration values to be provided:
access-exp-time
which indicates how long an access token is valid for,refresh-exp-time
which indicates how long a refresh token is valid for,- Optional property:
key-rotation-time
which indicates how often Key pairs are rotated. Rotation will be disabled if missing. alg-name
which indicates which algorithm is used to encode your keys.
To setup for AWS Secrets Manager, your config should look like so:
loginsvc:
rest:
jwt:
aws-secrets-manager:
secret-name: "secret"
region: "region"
private-key-field-name: "privateKey"
public-key-field-name: "publicKey"
access-exp-time: 15min
refresh-exp-time: 9h
poll-time: 30min
alg-name: "RS256"
Your AWS Secret must have at least 2 fields which correspond to the above properties:
private-key-field-name: "privateKey"
public-key-field-name: "publicKey"
with "privateKey"
and "publicKey"
indicating the field-name of those secrets.
Replace the above example values with the field-names you used in AWS Secrets Manager.
There are a few important configuration values to be provided:
access-exp-time
which indicates how long an access token is valid for,refresh-exp-time
which indicates how long a refresh token is valid for,- Optional property:
poll-time
which indicates how often key pairs (private-key-field-name
andpublic-key-field-name
) are polled and fetched from AWS Secrets Manager. Polling will be disabled if missing. alg-name
which indicates which algorithm is used to encode your keys.
Please note that only one configuration option (loginsvc.rest.jwt.{aws-secrets-manager|generate-in-memory}
) can be used at a time.
sbt jacoco
Code coverage will be generated on path:
{project-root}/{module}/target/scala-{scala_version}/jacoco/report/html
Springboot Actuator is enabled for this project. This provides the user with an endpoint (readable via HTTP or JMX) that describes the overall status of the login-service as well as its parts.
Health Endpoint can be accessed via http using the following URL: http://localhost:port/actuator/health
Accessing the above should give you the following Json message if all is functional and healthy:
{"status":"UP"}
If one of the monitored dependencies are unavailable or unhealthy then you will get:
{"status":"DOWN"}
If you wish for a full breakdown of the applications health including all dependencies then add the following to the application.properties file:
management.endpoint.health.show-details=always
The health endpoint is also available via the Swagger: http://localhost:port/swagger-ui.html
Local JMX is currently enabled on the project. In order to utilize it please follow the following steps:
- Start the login-service application.
- Open a terminal or command prompt and run the following command to start JConsole:
jconsole
- In the JConsole window that opens, select the process corresponding to the login-service application from the list of local processes.
- Click the Connect button. JConsole will connect to the JMX agent running in the login-service application.
- In the MBean tab, expand the org.springframework.boot domain to see the available JMX endpoints.
- Find the health endpoint and click on it to view its attributes and operations. You can use the attributes and operations to check the health of the login-service and perform management tasks. You can now use JConsole to monitor and manage your local application by accessing the available endpoints. Remote JMX is also an option and may be enabled with some config changes in the application.properties file.
Springboot Actuator is enabled for this project. This enables an Info endpoint that can be populated with various information that may be
useful to the troubleshooting and usage of the application. The endpoint can be accessed via the following url: http://localhost:port/actuator/info
.
The information types available and how to use them is shown in the example config (example.application.yaml
).
Running the example config will get you the following output:
{"app":{"name":"login-service","build":"0.1","description":"Application used a reusable authentication tool","env":"Dev"},"security":{"ldap":"Enabled"},"git":{"commit":{"id":{"full":"git_id"},"message":{"full":"Added Git Properties"},"user":{"email":"exampleuser@org.com"},"time":"5/15/2023"}}}
If you wish to change what is shown here, you can do so by changing the fields and attributes in the application file. More info on the Actuator Info Service can be found here: https://reflectoring.io/spring-boot-info-endpoint/
An example git.properties file has also been included (example.git.properties
), simply rename it to git.properties
in order to make use of it in the info endpoint.
If you wish to generate an accurate git.properties file
, you can do so in 2 ways:
- Setting the
loginsvc.rest.config.git-info.generate-git-properties
totrue
will display newly generated git information. Additionally settingloginsvc.rest.config.git-info.generate-git-properties-file
totrue
will generate a new file with the updated git information on application startup. - By manually adjusting and running the test in
za.co.absa.logingw.rest.actuator.tooling.GitPropertiesGenerator.scala
In order to run the test the line (line 30) that reads as:should be changed to:ignore should "generate git.properties file" in {
once this is done, running or debugging the test will generate a"This function" should "generate git.properties file" in {
git.properties
file to be used for the info endpoint.
This requires Git to be installed and available on the host.
The example git.properties
file provided may be edited manually if the git generation is functioning incorrectly.
See Readme in clientLibrary module.