Authentication and Authorization - Skullabs/kikaha GitHub Wiki
Kikaha has a very tiny and non-intrusive Security API written over Undertow's Security API. Basically it has a few components that together allow you to protect your resources. Let's checkout the main components you'll usually need to deal with:
- Authentication Rules: will define how your web resources will be protected against unauthorized access.
- Authentication Mechanisms: defines how to extract credentials from the request when trying to authenticate.
- Identity Manager: defines how to check if found credentials are valid.
- Session Store: defines how to sessions are managed on the server.
- Password Encoder: defines how passwords are stored and matched.
Authentication mechanisms is responsible for extract credentials from HTTP requests. Usually they does not check if the credentials are valid, but delegates the validation to the IdentityManager defined on the matched Authentication Rule. Out-of-box only a few Authentication Mechanisms are provided. They are very common and allow developers to get started very quickly. Bellow they are listed and identified by its own alias
:
- basic: The implementation of Http's Basic Authentication.
- form: The Form-based authentication. It will enforce security by requiring user to provide their credentials through a HTML form.
- json: Will receive the credentials through a HTTP POST of a JSON object. This mechanism requires kikaha-jackson module.
- default: Points to the "basic" one.
Now lets talk about its alias. All Authentication Mechanism should be defined on the configuration entry server.auth.authentication-mechanisms
on a alias: class.Name
form (see sample the configuration file bellow). The alias, as we discussed before, are used on the Authentication Rules and avoids you to repeat the Authentication Mechanism's Class Canonical Name every time you need to use it to protect a specific resource.
Here are a sample code with the default Authentication Mechanisms that comes out-of-box with Kikaha:
server:
auth:
auth-mechanisms:
# available on kikaha-core's module
default: kikaha.core.modules.security.BasicAuthenticationMechanism
basic: kikaha.core.modules.security.BasicAuthenticationMechanism
form: kikaha.core.modules.security.FormAuthenticationMechanism
# available on kikaha-jackson's module
json: kikaha.urouting.serializers.jackson.JSONAuthenticationMechanism
If none of the above described mechanisms fits your needs you can easily create your own by implementing the interface kikaha.core.modules.security.AuthenticationMechanism
and defining an alias for it. Please, keep in mind that cood Authentication Mechanisms usually delegates the Credential Validation to a proper IdentityManager.
Identity Managers are responsible for ensure credentials are valid. It is also responsible to retrieve the Account
and its Roles
from the place you've stored your data. Out-of-box there a few Identity Managers available:
- fixed: Uses a fixed username and password, defined on the configuration file, as the only valid credential
- db-auth: Uses a Database connection to validate credentials.
- default: Points to the "fixed" one.
A more sophisticated Identity Manager could be developed by implementing the kikaha.core.modules.security.IdentityManager
interface. A much simpler approach is to extends kikaha.core.modules.security.AbstractPasswordBasedIdentityManager
.
The code bellow illustrates how to create your own Identity Manager with fixed username and password. Developers are encouraged to follow this pattern to provide their own Identity Manager.
package sample;
import kikaha.core.modules.security.*;
public class CustomIdentityManager extends AbstractPasswordBasedIdentityManager {
final String username = "admin";
final String password = "password";
final String defaultRole = "admin";
@Override
public Account retrieveAccountFor( String id, String password ) {
if ( this.username.equals( id ) && this.password.equals( password ) )
return new FixedUsernameAndRolesAccount( username, defaultRole );
return null;
}
}
Don't forget to register an alias to your custom Identity Manager.
server:
auth:
identity-manager:
custom: sample.CustomIdentityManager
Whenever you need to handle how to store or match a password you can use a Password Encoder to keep you credentials safe. Basically, out-of-box, there are two password encoders available:
- plain-text: Keep your password as is. This is the default one. Class:
kikaha.core.modules.security.PlainTextPasswordEncoder
- bcrypt: uses BCrypt to protect your password. Class: 'kikaha.bcrypt.BCryptPasswordEncoder'
You can define which Password Encoder will be used on your application by simply pointing the configuration entry server.auth.password-encoder
to one of above mentioned classes.
server:
auth:
password-encoder: kikaha.core.modules.security.PlainTextPasswordEncoder
In order to protect your resources and forces the authentication process you should include an entry in server.auth.rules
array on your configuration file. It will behaves like ACL where the first rule that matches the request will be applied. If no rule was matched, then the route will be executed as non-authenticated resource. Bellow we defined a rule the require authentication for any request made at "/protected/" resource and its sub-locations.
server:
auth:
rules:
- pattern: "/protected/*"
identity-manager: ["custom"]
auth-mechanisms: ["basic"]
expected-roles: []
exclude-patterns: [ "*.ico","*.js" ]
As you can note, there is some attributes we can define for each rule:
- pattern: the unique mandatory attribute, it defines which resources will require authentication
- identity-manager: (optional) defines which Identity Manager will be used to match the sent credentials. If not define, the "default" one will be used.
- mechanisms: (optional) defines which Authentication Mechanisms will used to extract credentials. If not define, the "default" one will be used.
- expected-roles: (optional) which roles should the Identity Manager retrieves to authorize the access to resources protected by this rule. Empty value means no specific roles are actually required to execute this resource.
- exclude-patterns: (optional) is an array of patters that should ignored during the authorization/authentication process. There is cases where is easier to map a more generalist pattern and define which URLs should be unconsidered on the authentication/authorization process. It may be also useful when you have more than one authentication rule.
If for some reason you have to repeat some exclusion-patterns on every rule you made, you rather define then at server.auth.default-excluded-patterns
.
Also, you can simplify your configuration file by overriding the 'default' identity-manager
and authentication-mechanism
. Bellow a more usual authentication rules should look like.
server:
auth:
# Using my custom
identity-manager: { default: sample.CustomIdentityManager }
default-excluded-patterns: ["*.ico","*.css","*.js","*.png","*.jpg"]
rules:
# Any logged in user will be able to enter most part of the system
- { pattern: "/*", auth-mechanisms: ["basic","form"], exclude-patterns: ["/api/admin/*"] }
# Only administrators are allowed to access /api/admin/*
- { pattern: "/api/admin/*", auth-mechanisms: ["basic"], expected-roles: ["ADMIN"] }
TODO
If you opt to use the Fixed User and Password Identity Manager, you can set the expected username, password and role at server.auth.fixed-auth
entry of your configuration file. You can find the default values here.
server:
auth:
fixed-auth:
username: "admin"
password: "admin"
role: "admin"
Consider the bellow configuration.
server:
auth:
rules:
- { pattern: "/protected/*", auth-'mechanisms: ["form"] }
fixed-auth:
username: "admin"
password: "1q2w3e"
role: "admin"
endpoints:
login-page: "/auth/login.html"
error-page: "/auth/error.html"
permission-denied-page: "/auth/forbiden.html"
callback-url: "/auth/callback"
Here we've defined a set of configuration parameters related to Form Authentication. At the first block we which resources will be required authorization to access. Besides is impossible to set more than one Authentication Mechanism at once, we set only "form" to simplify our example.
As we defined the "default" identity-manager as our mechanism to validate the credentials sent by the HTTP client, we defined the username and password at the fixed-auth
block. The last block, form-auth
define which pages will be used during the authentication lifecycle:
- login-page: the HTML page containing the form where users will be able to write its credentials
- error-page: the HTML page that will notify the user the logging have failed
- permission-defined-page: the HTML page the will notify the user the login was OK but he does not have permission (specific roles) to access the requested page.
<!-- http://www.avajava.com/tutorials/lessons/how-do-i-use-form-authentication-with-tomcat.html -->
<form method="POST" action="/protected/j_security_check">
<table>
<tr>
<td colspan="2">Login to the Demo application:</td>
</tr>
<tr>
<td>Name:</td>
<td><input type="text" name="j_username" /></td>
</tr>
<tr>
<td>Password:</td>
<td><input type="password" name="j_password"/ ></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="Go" /></td>
</tr>
</table>
</form>
Above a sample login.html
. Java developers will be at home here once it follows the same standard for form authentication defined by Servlet API:
- The action must match the callback URL above defined
- The username field must be named j_username
- The password field must be named j_password.