Authentication

Authentication is the first part of the interaction with the REST API. It is required so that Jmix can identify the user, who performs the requests. This allows Jmix to impose permission / data-access constraints on the requests to ensure the user is only allowed to see/interact with the parts of the system that it should be.

Authentication Request

To interact with the Jmix REST API, it is required to authenticate through an OAuth2 flow. The following sequence diagram explains the general interaction with the REST API:

authentication diagram

First, the API client needs to request an access token through the authentication request. For that, you need to send a www-form-urlencoded request with the username and password as part of the form body to Jmix:

Authentication Request
POST http://localhost:8080/oauth/token
Authorization: Basic {{client_id}} {{client_secret}} (1)
Content-Type: application/x-www-form-urlencoded (2)

grant_type=password (3)
&username={{username}} (4)
&password={{password}}
1 The Authentication Request itself is authenticated with Basic Auth client_id and client_secret.
2 The content of the authentication request is application/x-www-form-urlencoded.
3 The grant_type is password to indicate the type of login.
4 The user credentials are provided as part of the form with the keys username and password.

After a successful login, Jmix returns the access token as part of the HTTP response:

HTTP/1.1 200
{
  "access_token": "CXE0w/9cOsnpSo8v2jEDoI8Qa3Y=",
  "token_type": "bearer",
  "refresh_token": "Hh2xCuZ7fgd35obagEBNGevF4ws=",
  "expires_in": 31535999,
  "scope": "rest-api",
  "OAuth2.SESSION_ID": "5C46CDF266E8C8C15372887830B74F59"
}

The attribute access_token contains the token, that you can use in further requests: CXE0w/9cOsnpSo8v2jEDoI8Qa3Y=.

Refresh Token

The token acts as a temporary credential/password. After a particular period, the token expires and you need to request a new token. The first option for requesting a new token to ask the user for their credentials again and perform a regular Authentication Request again.

Do not store the username and password in your client application so that the user does not need to re-enter its credentials. Either ask the user again for their credentials or use the refresh token to prevent token expiration.

The original Authentication Request contains a refresh_token, that you can use to create a new access token. While the access token is comparably short-lived (by default 12 hours for Jmix), the refresh token is valid for 365 days by default. Therefore, it is well suited as a long-lived credential to issue new tokens.

To get a new access token via the refresh token, you can use the same endpoint of obtaining a token. But instead of using the grand_type: password, you can also use refresh_token. Here is an example of how to use the refresh token to obtain a new access token:

Refresh Authentication Request
POST http://localhost:8080/oauth/token
Authorization: Basic {{client_id}} {{client_secret}}
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token (1)
&refresh_token=Hh2xCuZ7fgd35obagEBNGevF4ws= (2)
1 The grant_type is refresh_token to indicate the type of login that is performed.
2 The refresh token is provided as part of the form with the key refresh_token.

The response contains a new access token, that has a new expiration period. With that, you can keep access to the API without the need to ask the user for their credentials after the access token expires.

Making API Requests

There are two ways on how to use the access token to authenticate against the different Jmix APIs, depending on the use case. Let’s start with the first and main way of providing the access token: via an HTTP header in the following requests.

Authenticate via Header

With the received access token CXE0w/9cOsnpSo8v2jEDoI8Qa3Y=, you can now perform requests against the REST API. The access token needs to be passed in as the Authorization Header in every request, like in the following example:

Create Customers Request
POST http://localhost:8080/rest
            /entities
            /rstex11_Customer
Authorization: Bearer CXE0w/9cOsnpSo8v2jEDoI8Qa3Y=  (1)

{
  name: "Randall Bishop"
}
1 The API request is authenticated with the authorization type Bearer, and the received access token.

Authenticate via URL Parameter

It is also possible to pass the access token as a URL query parameter. This is necessary when the HTTP headers cannot be set. In case of the browser linking to a file or render an image.

In the following example an image from the Files API should be rendered via <img src="…​" /> on a website.

In this case, it is not possible to set the HTTP headers. Therefore, you can pass in the access_token as a URL as a query parameter:

<img
    src="http://localhost:8080/files
            ?access_token=CXE0w/9cOsnpSo8v2jEDoI8Qa3Y=
            &fileRef=fs://2021/03/12/a3b6011d-9040-151e-7d17-f7ccdf75d72f.jpg?name=cat.jpg"
/>

Anonymous Access

By default, all endpoints are only available after a successful authentication against the application. But it is also possible to expose certain parts of the REST API without authentication. This is possible by using the anonymous access functionality of Jmix. In this case, the API request is performed as the user anonymous, which is configured by default in a Jmix application.

For every secured endpoint that is called without the Authentication header, the user will be authenticated with the anonymous user session.

To whitelist specific endpoints for anonymous access, set a comma-separated list of URL patterns in the jmix.rest.anonymous-url-patterns application property. For example:

jmix.rest.anonymous-url-patterns = \
  /rest/services/productService/getProductInformation,\
  /rest/entities/Product,\
  /rest/entities/Product/*

The last pattern in the example above is needed if you want to update or delete the Product entity, because in this case the URL has the id part.

Once this setting is in place, it is possible to interact with the ProductService without sending an Authorization header:

GetProductInformation Request
GET {{baseRestUrl}}
         /services
         /productService
         /getProductInformation
         ?productId=123
# Authorization: not set

This request will respond in a successful response of the Service:

HTTP/1.1 200
{
  "name": "Apple iPhone",
  "productId": "123",
  "price": 499.99
}

If you want to provide anonymous access to some entities endpoints, make sure the anonymous user has rights to these entities. You can do it by creating a resource role and assigning it to the anonymous user in the DatabaseUserRepository.initAnonymousUser() method. For example:

@ResourceRole(name = "AnonymousRestRole", code = AnonymousRestRole.CODE, scope = "API")
public interface AnonymousRestRole {

    String CODE = "anonymous-rest-role";

    @EntityAttributePolicy(entityClass = Product.class,
        attributes = "*",
        action = EntityAttributePolicyAction.MODIFY)
    @EntityPolicy(entityClass = Product.class,
        actions = {EntityPolicyAction.READ, EntityPolicyAction.UPDATE})
    void product();
}
@Primary
@Component("UserRepository")
public class DatabaseUserRepository extends AbstractDatabaseUserRepository<User> {
    // ...

    @Override
    protected void initAnonymousUser(User anonymousUser) {
        Collection<GrantedAuthority> authorities = getGrantedAuthoritiesBuilder()
                .addResourceRole(AnonymousRestRole.CODE)
                .build();
        anonymousUser.setAuthorities(authorities);
    }
}
The anonymous access feature does not require that anonymous user has the rest-minimal role.