For DSpace 7 the REST authentication has been rewritten from the ground up. It makes use of Spring Security and JSON Web tokens to support stateless sessions.
To authenticate yourself, you have to send a POST request to the /api/authn/login endpoint with the following parameters:
parameter | value |
---|---|
user | email/id of user |
password | password of user |
Example call with curl:
curl -v -X POST --data "user=test@dspace.com&password=p4ssword" "http://{spring-rest.url}/api/authn/login"
This call will return a JWT (JSON Web Token) in the response in the Authorization header according to the bearer scheme, this token has to be used in subsequent calls to provide your authentication details.
The HAL browser has been extended to support login in. You can find the login in the navigation where you can enter your credentials. After supplying valid credentials the token will be saved in a cookie and every request will get this token from the cookie and send it in the authorization header.
For DSpace to detect the token it has to be send in the Authorization header with the Bearer schema, that is prepend the token with "Bearer" then leave a space and paste the token.
The authentication status can be checked by sending your received token to the status endpoint in the Authorization header:
curl -v "http://{spring-rest.url}/api/authn/status" -H "Authorization: Bearer eyJhbG...COdbo"
This will return the authentication status, E.G.:
{
"okay" : true,
"authenticated" : true,
"type" : "status",
"_links" : {
"eperson" : {
"href" : "http://localhost:8080/dspace7-rest/api/eperson/epersons/2245f2c5-1bed-414b-a313-3fd2d2ec89d6"
}
},
"_embedded" : {
"eperson" : {
"uuid" : "2245f2c5-1bed-414b-a313-3fd2d2ec89d6",
"email" : "test@dspace.com",
...
}
}
}
}
Field | Meaning |
---|---|
Okay | True if rest api is up and running, should never return false |
Authenticated | True if the token is valid, false if there was no token or the token wasn't valid |
Type | Type of the endpoint, "status" in this case |
_links | returns a link to the authenticated eperson |
_embedded | Embeds the authenticated eperson |
To logout and invalidate the token, send the token in the Authorization header with the bearer scheme to the following endpoint:
/api/authn/logout
E.G.
curl -v "http://{spring-rest.url}/api/authn/logout" -H "Authorization: Bearer eyJhbG...COdbo"
This will log the user out on every device/browser.
The authentication token is JWT and is base64url encoded. For more information about JWT: https://jwt.io/introduction/
By default the JWT token will have a couple of claims already, which we can see if we decode the token:
Claim | Data |
---|---|
eid | Contains the id of the eperson |
sg | Contains the id's of the special groups to which a user belongs |
exp | Contains the expiration date when a token will expire |
Extra claims can be added by creating more beans which implement the JWTClaimProvider interface. Spring will scan for these and use them to automatically add new claims to the tokens.
The JWTClaimProvider interface requires three methods to be implemented:
getKey(): String
This method should return a string, this string will be used as key for the claim (for example "eid" for the eperson id claim)
getValue(Context, HttpServletRequest): Object
This method should return the value of the claim, This can be any object, as long as it is Serialisable.
parseClaim(Context, HttpServletRequest, JWTClaimSet)
This method should parse the claim when someone issues a token. In this method you should handle what has to happen with it (for example setting special groups on the context object)
NOTE: add @Component to your ClaimProviders so Spring can find them.
When a token is about to expire (Which can be checked with the exp claim), you can request a new token with a new expiration time (by default 30 minutes). To do so send the token to the login endpoint without "user" and "password" parameters. As a response you'll get a new freshly issued token (again in the Authorization header of the response).
E.G.
curl -v "http://{spring-rest.url}/api/authn/login" -H "Authorization: Bearer eyJhbG...COdbo"
Which will return something like this:
HTTP/1.1 200 OK
Authorization: Bearer eyJhbG...COdbo
Now you can use this new token to continue making authenticated requests.
The new stateless authentication introduced a few new properties that can be configured, these can all be found under modules/authentication.cfg:
jwt.token.secret | Manually define a key here that will be used to sign the tokens. If this is empty a random key will be generated. Note that if you want to run DSpace in a cluster with multiple instances this has to be configured and every instance has to use the same key |
jwt.encryption.enabled | Boolean property, defaults to false. If enabled the tokens will be encrypted and unreadable clientside. As a downside enabling this makes the tokens a bit larger which will make the size of requests a bit larger, another disadvantage is not being able to use the data that is inside the token clientside |
jwt.encryption.secret | Key to use if encryption for jwt is enabled, if none is specified DSpace will generate a random one, but when using clusters and encryption then one should be configured |
jwt.token.expiration | Enter the time in minutes that a token should be valid for, by default this is 30 |
jwt.token.include.ip | Boolean property, if true then the signing key will also use the ip-address of the user, this will prevent the same token from being used from another ip-address. Defaults to true |
One of the biggest advantages of this stateless authentication is that we can scale horizontally and run multiple instances of DSpace and be able to send the same token to all of them. To do so a few properties have to be configured:
jwt.token.secret: This property has to be the same on every instance of DSpace. It's best to choose a long, non trivial one for extra security.
if jwt.encryption.enabled is set to true then jwt.encryption.secret also has to be configured and the same key has to be used on every instance.
The signing key of the tokens is constructed by
The salt is saved in the EPerson table in the database and is used for:
Invalidating tokens: When someone logs out the salt will be removed from the database, so that the token won't be valid anymore since the sign key can't be constructed anymore
Making sure the signing key has a valid length: The salt is always 32 bytes and the key to sign is required to be >32 bytes