# Authorization of web endpoints

Quarkus incorporates a pluggable web security layer. When security is active, the system performs a permission check on all HTTP requests to determine if they should proceed.

Using `@PermitAll` will not open a path if the path is restricted by the `quarkus.http.auth.` configuration. To ensure specific paths are accessible, appropriate configurations must be made within the Quarkus security settings.

| If you use Jakarta RESTful Web Services, consider using `quarkus.security.jaxrs.deny-unannotated-endpoints` or `quarkus.security.jaxrs.default-roles-allowed` to set default security requirements instead of HTTP path-level matching because annotations can override these properties on an individual endpoint.

Authorization is based on user roles that the security provider provides. To customize these roles, a `SecurityIdentityAugmentor` can be created, see Security Identity Customization.

## Authorization using configuration

Permissions are defined in the Quarkus configuration by permission sets, each specifying a policy for access control.

Table 1. Quarkus policies summary
Built-in policy | Description
--- | ---
`deny` | This policy denies all users.
`permit` | This policy permits all users.
`authenticated` | This policy permits only authenticated users.

You can define role-based policies that allow users with specific roles to access the resources.

Example of a role-based policy

    quarkus.http.auth.policy.role-policy1.roles-allowed=user,admin                  (1)

1 | This defines a role-based policy that allows users with the `user` and `admin` roles.

You can reference a custom policy by configuring the built-in permission sets that are defined in the `application.properties` file, as outlined in the following configuration example:

Example of policy configuration

    quarkus.http.auth.permission.permit1.paths=/public/*                            (1)
    quarkus.http.auth.permission.permit1.policy=permit
    quarkus.http.auth.permission.permit1.methods=GET

    quarkus.http.auth.permission.deny1.paths=/forbidden                             (2)
    quarkus.http.auth.permission.deny1.policy=deny

    quarkus.http.auth.permission.roles1.paths=/roles-secured/*,/other/*,/api/*      (3)
    quarkus.http.auth.permission.roles1.policy=role-policy1

1 | This permission references the default built-in `permit` policy to allow `GET` methods to `/public`. In this case, the demonstrated setting would not affect this example because this request is allowed anyway.
2 | This permission references the built-in `deny` policy for both `/forbidden` and `/forbidden/` paths. It is an exact path match because it does not end with `*`.
3 | This permission set references the previously defined policy. `roles1` is an example name; you can call the permission sets whatever you want.

| The exact path pattern `/forbidden` in the example above also secures the `/forbidden/` path. This way, the `forbidden` endpoint in the example below is secured by the `deny1` permission.
    package org.acme.crud;

    import jakarta.ws.rs.GET;
    import jakarta.ws.rs.Path;

    @Path("/forbidden")
    public class ForbiddenResource {
        @GET
        public String forbidden() { (1)
            return "No!";
        }
    }
1 | Both `/forbidden` and `/forbidden/` paths need to be secured in order to secure the `forbidden` endpoint.
If you need to permit access to the `/forbidden/` path, please add new permission with more specific exact path like in the example below:
    quarkus.http.auth.permission.permit1.paths=/forbidden/ (1)
    quarkus.http.auth.permission.permit1.policy=permit
1 | The `/forbidden/` path is not secured.

### Custom HttpSecurityPolicy

Sometimes it might be useful to register your own named policy. You can get it done by creating application scoped CDI bean that implements the `io.quarkus.vertx.http.runtime.security.HttpSecurityPolicy` interface like in the example below:

    import jakarta.enterprise.context.ApplicationScoped;

    import io.quarkus.security.identity.SecurityIdentity;
    import io.quarkus.vertx.http.runtime.security.HttpSecurityPolicy;
    import io.smallrye.mutiny.Uni;
    import io.vertx.ext.web.RoutingContext;

    @ApplicationScoped
    public class CustomNamedHttpSecPolicy implements HttpSecurityPolicy {
        @Override
        public Uni<CheckResult> checkPermission(RoutingContext event, Uni<SecurityIdentity> identity,
                AuthorizationRequestContext requestContext) {
            if (customRequestAuthorization(event)) {
                return CheckResult.permit();
            }
            return CheckResult.deny();
        }

        @Override
        public String name() {
            return "custom"; (1)
        }

        private static boolean customRequestAuthorization(RoutingContext event) {
            // here comes your own security check
            return !event.request().path().endsWith("denied");
        }
    }

1 | Named HTTP Security policy will only be applied to requests matched by the `application.properties` path matching rules.

Example of custom named HttpSecurityPolicy referenced from configuration file

    quarkus.http.auth.permission.custom1.paths=/custom/*
    quarkus.http.auth.permission.custom1.policy=custom                              (1)

1 | Custom policy name must match the value returned by the `io.quarkus.vertx.http.runtime.security.HttpSecurityPolicy.name` method.

Alternatively, you can bind custom named HttpSecurityPolicy to Jakarta REST endpoints with the `@AuthorizationPolicy` security annotation.

Example of custom named HttpSecurityPolicy bound to a Jakarta REST endpoint

    import io.quarkus.vertx.http.security.AuthorizationPolicy;
    import jakarta.annotation.security.DenyAll;
    import jakarta.ws.rs.GET;
    import jakarta.ws.rs.Path;

    @DenyAll (1)
    @Path("hello")
    public class HelloResource {

        @AuthorizationPolicy(name = "custom") (2)
        @GET
        public String hello() {
            return "hello";
        }

    }

1 | The `@AuthorizationPolicy` annotation can be used together with other standard security annotations. As usual, the method-level annotation has priority over class-level annotation.
2 | Apply custom named HttpSecurityPolicy to the Jakarta REST `hello` endpoint.

| You can also create global `HttpSecurityPolicy` invoked on every request. Just do not implement the `io.quarkus.vertx.http.runtime.security.HttpSecurityPolicy.name` method and leave the policy nameless.

### Inject `@RequestScoped` beans into `HttpSecurityPolicy`

`@RequestScoped` beans can only be injected when the CDI request context is active. The context can be activated by users, for example with the `@ActivateRequestContext`, however authorization happens before Quarkus prepares some `@RequestScoped` beans. We recommend to let Quarkus activate and prepare CDI request context for you. For example, consider a situation where you want to inject a bean from the Jakarta REST context, such as the `jakarta.ws.rs.core.UriInfo` bean. In this case, you must apply the `HttpSecurityPolicy` to Jakarta REST endpoints. This can be achieved in one of the following ways: * Use the `@AuthorizationPolicy` security annotation. * Set the `quarkus.http.auth.permission.custom1.applies-to=jaxrs` configuration property.

### Matching on paths and methods

Permission sets can also specify paths and methods as a comma-separated list. If a path ends with the `*` wildcard, the query it generates matches all sub-paths. Otherwise, it queries for an exact match and only matches that specific path:

    quarkus.http.auth.permission.permit1.paths=/public*,/css/*,/js/*,/robots.txt    (1)
    quarkus.http.auth.permission.permit1.policy=permit
    quarkus.http.auth.permission.permit1.methods=GET,HEAD

1 | The `*` wildcard at the end of the path matches zero or more path segments, but never any word starting from the `/public` path. For that reason, a path like `/public-info` is not matched by this pattern.

### Matching a path but not a method

The request is rejected if it matches one or more permission sets based on the path but none of the required methods.

| Given the preceding permission set, `GET /public/foo` would match both the path and method and therefore be allowed. In contrast, `POST /public/foo` would match the path but not the method, and, therefore, be rejected.

### Matching multiple paths: longest path wins

Matching is always done on the "longest path wins" basis. Less specific permission sets are not considered if a more specific one has been matched:

    quarkus.http.auth.permission.permit1.paths=/public/*
    quarkus.http.auth.permission.permit1.policy=permit
    quarkus.http.auth.permission.permit1.methods=GET,HEAD

    quarkus.http.auth.permission.deny1.paths=/public/forbidden-folder/*
    quarkus.http.auth.permission.deny1.policy=deny

| Given the preceding permission set, `GET /public/forbidden-folder/foo` would match both permission sets' paths. However, because the longer path matches the path of the `deny1` permission set, `deny1` is chosen, and the request is rejected.

| Subpath permissions precede root path permissions, as the `deny1` versus `permit1` permission example previously illustrated. This rule is further exemplified by a scenario where subpath permission allows access to a public resource while the root path permission necessitates authorization.
    quarkus.http.auth.policy.user-policy.roles-allowed=user
    quarkus.http.auth.permission.roles.paths=/api/*
    quarkus.http.auth.permission.roles.policy=user-policy

    quarkus.http.auth.permission.public.paths=/api/noauth/*
    quarkus.http.auth.permission.public.policy=permit

### Matching multiple sub-paths: longest path to the `*` wildcard wins

Previous examples demonstrated matching all sub-paths when a path concludes with the `*` wildcard.

This wildcard also applies in the middle of a path, representing a single path segment. It cannot be mixed with other path segment characters; thus, path separators always enclose the `*` wildcard, as seen in the `/public/*/about-us` path.

When several path patterns correspond to the same request path, the system selects the longest sub-path leading to the `*` wildcard. In this context, every path segment character is more specific than the `*` wildcard.

Here is a simple example:

    quarkus.http.auth.permission.secured.paths=/api/*/detail                    (1)
    quarkus.http.auth.permission.secured.policy=authenticated
    quarkus.http.auth.permission.public.paths=/api/public-product/detail        (2)
    quarkus.http.auth.permission.public.policy=permit

1 | Request paths like `/api/product/detail` can only be accessed by authenticated users.
2 | The path `/api/public-product/detail` is more specific, therefore accessible by anyone.

| All paths secured with the authorization using configuration should be tested. Writing path patterns with multiple wildcards can be cumbersome. Please make sure paths are authorized as you intended.

In the following example, paths are ordered from the most specific to the least specific one:

Request path `/one/two/three/four/five` matches ordered from the most specific to the least specific path

    /one/two/three/four/five
    /one/two/three/four/*
    /one/two/three/*/five
    /one/two/three/*/*
    /one/two/*/four/five
    /one/*/three/four/five
    /*/two/three/four/five
    /*/two/three/*/five
    /*

| The `*` wildcard at the end of the path matches zero or more path segments. The `*` wildcard placed anywhere else matches exactly one path segment.

### Matching multiple paths: most specific method wins

When a path is registered with multiple permission sets, the permission sets explicitly specifying an HTTP method that matches the request take precedence. In this instance, the permission sets without methods only come into effect if the request method does not match permission sets with the method specification.

    quarkus.http.auth.permission.permit1.paths=/public/*
    quarkus.http.auth.permission.permit1.policy=permit
    quarkus.http.auth.permission.permit1.methods=GET,HEAD

    quarkus.http.auth.permission.deny1.paths=/public/*
    quarkus.http.auth.permission.deny1.policy=deny

| The preceding permission set shows that `GET /public/foo` matches the paths of both permission sets.However, it specifically aligns with the explicit method of the `permit1` permission set.Therefore, `permit1` is selected, and the request is accepted. In contrast, `PUT /public/foo` does not match the method permissions of `permit1`. As a result, `deny1` is activated, leading to the rejection of the request.

### Matching multiple paths and methods: both win

Sometimes, the previously described rules allow multiple permission sets to win simultaneously. In that case, for the request to proceed, all the permissions must allow access. For this to happen, both must either have specified the method or have no method. Method-specific matches take precedence.

    quarkus.http.auth.policy.user-policy1.roles-allowed=user
    quarkus.http.auth.policy.admin-policy1.roles-allowed=admin

    quarkus.http.auth.permission.roles1.paths=/api/*,/restricted/*
    quarkus.http.auth.permission.roles1.policy=user-policy1

    quarkus.http.auth.permission.roles2.paths=/api/*,/admin/*
    quarkus.http.auth.permission.roles2.policy=admin-policy1

| Given the preceding permission set, `GET /api/foo` would match both permission sets' paths, requiring both the `user` and `admin` roles.

### Configuration properties to deny access

The following configuration settings alter the role-based access control (RBAC) denying behavior:

`quarkus.security.jaxrs.deny-unannotated-endpoints=true|false`

  If set to true, access is denied for all Jakarta REST endpoints by default. If a Jakarta REST endpoint has no security annotations, it defaults to the `@DenyAll` behavior. This helps you to avoid accidentally exposing an endpoint that is supposed to be secured. Defaults to `false`.

`quarkus.security.jaxrs.default-roles-allowed=role1,role2`

  Defines the default role requirements for unannotated endpoints. The `**` role is a special role that means any authenticated user. This cannot be combined with `deny-unannotated-endpoints` because `deny` takes effect instead.

`quarkus.security.deny-unannotated-members=true|false`

  If set to true, the access is denied to all CDI methods and Jakarta REST endpoints that do not have security annotations but are defined in classes that contain methods with security annotations. Defaults to `false`.

### Disabling permissions

Permissions can be disabled at build time with an `enabled` property for each declared permission, such as:

    quarkus.http.auth.permission.permit1.enabled=false
    quarkus.http.auth.permission.permit1.paths=/public/*,/css/*,/js/*,/robots.txt
    quarkus.http.auth.permission.permit1.policy=permit
    quarkus.http.auth.permission.permit1.methods=GET,HEAD

Permissions can be reenabled at runtime with a system property or environment variable, such as: `-Dquarkus.http.auth.permission.permit1.enabled=true`.

### Permission paths and HTTP root path

The `quarkus.http.root-path` configuration property changes the http endpoint context path.

By default, `quarkus.http.root-path` is prepended automatically to configured permission paths then do not use a forward slash, for example:

    quarkus.http.auth.permission.permit1.paths=public/*,css/*,js/*,robots.txt

This configuration is equivalent to the following:

    quarkus.http.auth.permission.permit1.paths=${quarkus.http.root-path}/public/*,${quarkus.http.root-path}/css/*,${quarkus.http.root-path}/js/*,${quarkus.http.root-path}/robots.txt

A leading slash changes how the configured permission path is interpreted. The configured URL is used as-is, and paths are not adjusted if the value of `quarkus.http.root-path` changes.

Example:

    quarkus.http.auth.permission.permit1.paths=/public/*,css/*,js/*,robots.txt

This configuration only impacts resources served from the fixed or static URL, `/public`, which might not match your application resources if `quarkus.http.root-path` has been set to something other than `/`.

For more information, see Path Resolution in Quarkus.

### Map `SecurityIdentity` roles

Winning role-based policy can map the `SecurityIdentity` roles to the deployment-specific roles. These roles are then applicable for endpoint authorization by using the `@RolesAllowed` annotation.

    quarkus.http.auth.policy.admin-policy1.roles.admin=Admin1 (1)
    quarkus.http.auth.permission.roles1.paths=/* (2)
    quarkus.http.auth.permission.roles1.policy=admin-policy1

1 | Map the `admin` role to `Admin1` role. The `SecurityIdentity` will have both `admin` and `Admin1` roles.
2 | The `/*` path is secured, only authenticated HTTP requests are granted access.

If all that you need is to map the `SecurityIdentity` roles to the deployment-specific roles regardless of a path, you can also do this:

    quarkus.http.auth.roles-mapping.admin=Admin1  (1) (2)

1 | Map the `admin` role to `Admin1` role. The `SecurityIdentity` will have both `admin` and `Admin1` roles.
2 | The `/*` path is not secured. You must secure your endpoints with standard security annotations or define HTTP permissions in addition to this configuration property.

### Shared permission checks

One important rule for unshared permission checks is that only one path match is applied, the most specific one. Naturally you can specify as many permissions with the same winning path as you want and they will all be applied. However, there can be permission checks you want to apply to many paths without repeating them over and over again. That’s where shared permission checks come in, they are always applied when the permission path is matched.

Example of custom named HttpSecurityPolicy applied on every HTTP request

    quarkus.http.auth.permission.custom1.paths=/*
    quarkus.http.auth.permission.custom1.shared=true    (1)
    quarkus.http.auth.permission.custom1.policy=custom

    quarkus.http.auth.policy.admin-policy1.roles-allowed=admin
    quarkus.http.auth.permission.roles1.paths=/admin/*
    quarkus.http.auth.permission.roles1.policy=admin-policy1

1 | Custom HttpSecurityPolicy will be also applied on the `/admin/1` path together with the `admin-policy1` policy.

| Configuring many shared permission checks is less effective than configuring unshared ones. Use shared permissions to complement unshared permission checks like in the example below.

Map `SecurityIdentity` roles with shared permission

    quarkus.http.auth.policy.role-policy1.roles.root=admin,user (1)
    quarkus.http.auth.permission.roles1.paths=/secured/*        (2)
    quarkus.http.auth.permission.roles1.policy=role-policy1
    quarkus.http.auth.permission.roles1.shared=true

    quarkus.http.auth.policy.role-policy2.roles-allowed=user    (3)
    quarkus.http.auth.permission.roles2.paths=/secured/user/*
    quarkus.http.auth.permission.roles2.policy=role-policy2

    quarkus.http.auth.policy.role-policy3.roles-allowed=admin
    quarkus.http.auth.permission.roles3.paths=/secured/admin/*
    quarkus.http.auth.permission.roles3.policy=role-policy3

1 | Role `root` will be able to access `/secured/user/*` and `/secured/admin/*` paths.
2 | The `/secured/*` path can only be accessed by authenticated users. This way, you have secured the `/secured/all` path and so on.
3 | Shared permissions are always applied before unshared ones, therefore a `SecurityIdentity` with the `root` role will have the `user` role as well.

## Authorization using annotations

Quarkus includes built-in security to allow for Role-Based Access Control (RBAC) based on the common security annotations `@RolesAllowed`, `@DenyAll`, `@PermitAll` on REST endpoints and CDI beans.

Table 2. Quarkus annotation types summary
Annotation type | Description
--- | ---
`@DenyAll` | Specifies that no security roles are allowed to invoke the specified methods.
`@PermitAll` | Specifies that all security roles are allowed to invoke the specified methods. `@PermitAll` lets everybody in, even without authentication.
`@RolesAllowed` | Specifies the list of security roles allowed to access methods in an application.
`@Authenticated` | Quarkus provides the `io.quarkus.security.Authenticated` annotation that permits any authenticated user to access the resource. It’s equivalent to `@RolesAllowed("**")`.
`@PermissionsAllowed` | Specifies the list of permissions that are allowed to invoke the specified methods.
`@AuthorizationPolicy` | Specifies named `io.quarkus.vertx.http.runtime.security.HttpSecurityPolicy` that should authorize access to the specified Jakarta REST endpoints. Named HttpSecurityPolicy can be used for general authorization checks as demonstrated by Example of custom named HttpSecurityPolicy bound to a Jakarta REST endpoint.

The following SubjectExposingResource example demonstrates an endpoint that uses both Jakarta REST and Common Security annotations to describe and secure its endpoints.

SubjectExposingResource example

    import java.security.Principal;

    import jakarta.annotation.security.DenyAll;
    import jakarta.annotation.security.PermitAll;
    import jakarta.annotation.security.RolesAllowed;
    import jakarta.ws.rs.GET;
    import jakarta.ws.rs.Path;
    import jakarta.ws.rs.core.Context;
    import jakarta.ws.rs.core.SecurityContext;

    @Path("subject")
    public class SubjectExposingResource {

        @GET
        @Path("secured")
        @RolesAllowed("Tester") (1)
        public String getSubjectSecured(@Context SecurityContext sec) {
            Principal user = sec.getUserPrincipal(); (2)
            String name = user != null ? user.getName() : "anonymous";
            return name;
        }

        @GET
        @Path("authenticated")
        @Authenticated (3)
        public String getSubjectAuthenticated(@Context SecurityContext sec) {
            Principal user = sec.getUserPrincipal();
            String name = user != null ? user.getName() : "anonymous";
            return name;
        }

        @GET
        @Path("unsecured")
        @PermitAll (4)
        public String getSubjectUnsecured(@Context SecurityContext sec) {
            Principal user = sec.getUserPrincipal(); (5)
            String name = user != null ? user.getName() : "anonymous";
            return name;
        }

        @GET
        @Path("denied")
        @DenyAll (6)
        public String getSubjectDenied(@Context SecurityContext sec) {
            Principal user = sec.getUserPrincipal();
            String name = user != null ? user.getName() : "anonymous";
            return name;
        }
    }

1 | The `/subject/secured` endpoint requires an authenticated user with the granted "Tester" role through the use of the `@RolesAllowed("Tester")` annotation.
2 | The endpoint obtains the user principal from the Jakarta REST `SecurityContext`. This returns `non-null` for a secured endpoint.
3 | The `/subject/authenticated` endpoint allows any authenticated user by specifying the `@Authenticated` annotation.
4 | The `/subject/unsecured` endpoint allows for unauthenticated access by specifying the `@PermitAll` annotation.
5 | The call to obtain the user principal returns `null` if the caller is unauthenticated and `non-null` if the caller is authenticated.
6 | The `/subject/denied` endpoint declares the `@DenyAll` annotation, disallowing all direct access to it as a REST method, regardless of the user calling it. The method is still invokable internally by other methods in this class.

| If you plan to use standard security annotations on the IO thread, review the information in Proactive Authentication.

The `@RolesAllowed` annotation value supports property expressions including default values and nested property expressions. Configuration properties used with the annotation are resolved at runtime.

Table 3. Annotation value examples
Annotation | Value explanation
--- | ---
`@RolesAllowed("${admin-role}")` | The endpoint allows users with the role denoted by the value of the `admin-role` property.
`@RolesAllowed("${tester.group}-${tester.role}")` | An example showing that the value can contain multiple variables.
`@RolesAllowed("${customer:User}")` | A default value demonstration. The required role is denoted by the value of the `customer` property. However, if that property is not specified, a role named `User` is required as a default.

Example of a property expressions usage in the `@RolesAllowed` annotation

    admin=Administrator
    tester.group=Software
    tester.role=Tester
    %prod.secured=User
    %dev.secured=**
    all-roles=Administrator,Software,Tester,User

Subject access control example

    import java.security.Principal;

    import jakarta.annotation.security.RolesAllowed;
    import jakarta.ws.rs.GET;
    import jakarta.ws.rs.Path;
    import jakarta.ws.rs.core.Context;
    import jakarta.ws.rs.core.SecurityContext;

    @Path("subject")
    public class SubjectExposingResource {

        @GET
        @Path("admin")
        @RolesAllowed("${admin}") (1)
        public String getSubjectSecuredAdmin(@Context SecurityContext sec) {
            return getUsername(sec);
        }

        @GET
        @Path("software-tester")
        @RolesAllowed("${tester.group}-${tester.role}") (2)
        public String getSubjectSoftwareTester(@Context SecurityContext sec) {
            return getUsername(sec);
        }

        @GET
        @Path("user")
        @RolesAllowed("${customer:User}") (3)
        public String getSubjectUser(@Context SecurityContext sec) {
            return getUsername(sec);
        }

        @GET
        @Path("secured")
        @RolesAllowed("${secured}") (4)
        public String getSubjectSecured(@Context SecurityContext sec) {
            return getUsername(sec);
        }

        @GET
        @Path("list")
        @RolesAllowed("${all-roles}") (5)
        public String getSubjectList(@Context SecurityContext sec) {
            return getUsername(sec);
        }

        private String getUsername(SecurityContext sec) {
            Principal user = sec.getUserPrincipal();
            String name = user != null ? user.getName() : "anonymous";
            return name;
        }
    }

1 | The `@RolesAllowed` annotation value is set to the value of `Administrator`.
2 | This `/subject/software-tester` endpoint requires an authenticated user that has been granted the role of "Software-Tester". It is possible to use multiple expressions in the role definition.
3 | This `/subject/user` endpoint requires an authenticated user that has been granted the role "User" through the use of the `@RolesAllowed("${customer:User}")` annotation because we did not set the configuration property `customer`.
4 | In production, this `/subject/secured` endpoint requires an authenticated user with the `User` role. In development mode, it allows any authenticated user.
5 | Property expression `all-roles` will be treated as a collection type `List`, therefore, the endpoint will be accessible for roles `Administrator`, `Software`, `Tester` and `User`.

### Endpoint security annotations and Jakarta REST inheritance

Quarkus supports security annotations placed on the endpoint implementation or its class like in the example below:

    @Path("hello")
    public interface HelloInterface {

        @GET
        String hello();

    }

    @DenyAll (1)
    public class HelloInterfaceImpl implements HelloInterface {

        @RolesAllowed("admin") (2)
        @Override
        public String hello() {
            return "Hello";
        }
    }

1 | Class-level security annotation must be placed on the class where the endpoint implementation is declared.
2 | Method-level security annotation must be placed on the endpoint implementation.

| The RESTEasy subresource locators declared as default interface methods cannot be secured by standard security annotations. Secured subresource locators must be implemented and secured on the interface implementors like in the example below:
    @Path("hello")
    public interface HelloInterface {

        @RolesAllowed("admin")
        @Path("sub")
        default HelloSubResource wrongWay() {
            // not supported
        }

        @Path("sub")
        HelloSubResource rightWay();

    }

    public class HelloInterfaceImpl implements HelloInterface {

        @RolesAllowed("admin")
        @Override
        public HelloSubResource rightWay() {
            return new HelloSubResource();
        }
    }

### Permission annotation

Quarkus also provides the `io.quarkus.security.PermissionsAllowed` annotation, which authorizes any authenticated user with the given permission to access the resource. This annotation is an extension of the common security annotations and checks the permissions granted to a `SecurityIdentity` instance.

Example of endpoints secured with the `@PermissionsAllowed` annotation

    package org.acme.crud;

    import io.quarkus.arc.Arc;
    import io.vertx.ext.web.RoutingContext;
    import jakarta.ws.rs.GET;
    import jakarta.ws.rs.POST;
    import jakarta.ws.rs.Path;
    import jakarta.ws.rs.QueryParam;

    import io.quarkus.security.PermissionsAllowed;

    import java.security.BasicPermission;
    import java.security.Permission;
    import java.util.Collection;
    import java.util.Collections;

    @Path("/crud")
    public class CRUDResource {

        @PermissionsAllowed("create") (1)
        @PermissionsAllowed("update")
        @POST
        @Path("/modify/repeated")
        public String createOrUpdate() {
            return "modified";
        }

        @PermissionsAllowed(value = {"create", "update"}, inclusive=true) (2)
        @POST
        @Path("/modify/inclusive")
        public String createOrUpdate(Long id) {
            return id + " modified";
        }

        @PermissionsAllowed({"see:detail", "see:all", "read"}) (3)
        @GET
        @Path("/id/{id}")
        public String getItem(String id) {
            return "item-detail-" + id;
        }

        @PermissionsAllowed(value = "list", permission = CustomPermission.class) (4)
        @Path("/list")
        @GET
        public Collection<String> list(@QueryParam("query-options") String queryOptions) {
            // your business logic comes here
            return Collections.emptySet();
        }

        public static class CustomPermission extends BasicPermission {

            public CustomPermission(String name) {
                super(name);
            }

            @Override
            public boolean implies(Permission permission) {
                var event = Arc.container().instance(RoutingContext.class).get(); (5)
                var publicContent = "public-content".equals(event.request().params().get("query-options"));
                var hasPermission = getName().equals(permission.getName());
                return hasPermission && publicContent;
            }
        }
    }

1 | The resource method `createOrUpdate` is only accessible for a user with both `create` and `update` permissions.
2 | By default, at least one of the permissions specified through one annotation instance is required. You can require all permissions by setting `inclusive=true`. Both resource methods `createOrUpdate` have equal authorization requirements.
3 | Access is granted to `getItem` if `SecurityIdentity` has either `read` permission or `see` permission and one of the `all` or `detail` actions.
4 | You can use your preferred `java.security.Permission` implementation. By default, string-based permission is performed by `io.quarkus.security.StringPermission`.
5 | Permissions are not beans, therefore the only way to obtain bean instances is programmatically by using `Arc.container()`.

| If you plan to use the `@PermissionsAllowed` on the IO thread, review the information in Proactive Authentication.

| `@PermissionsAllowed` is not repeatable on the class level due to a limitation with Quarkus interceptors. For more information, see the Repeatable interceptor bindings section of the Quarkus "CDI reference" guide.

The easiest way to add permissions to a role-enabled `SecurityIdentity` instance is to map roles to permissions. Use Authorization using configuration to grant the required `SecurityIdentity` permissions for `CRUDResource` endpoints to authenticated requests, as outlined in the following example:

    quarkus.http.auth.policy.role-policy1.permissions.user=see:all                                      (1)
    quarkus.http.auth.policy.role-policy1.permissions.admin=create,update,read                          (2)
    quarkus.http.auth.permission.roles1.paths=/crud/modify/*,/crud/id/*                                 (3)
    quarkus.http.auth.permission.roles1.policy=role-policy1

    quarkus.http.auth.policy.role-policy2.permissions.user=list
    quarkus.http.auth.policy.role-policy2.permission-class=org.acme.crud.CRUDResource$CustomPermission  (4)
    quarkus.http.auth.permission.roles2.paths=/crud/list
    quarkus.http.auth.permission.roles2.policy=role-policy2

1 | Add the permission `see` and the action `all` to the `SecurityIdentity` instance of the `user` role. Similarly, for the `@PermissionsAllowed` annotation, `io.quarkus.security.StringPermission` is used by default.
2 | Permissions `create`, `update`, and `read` are mapped to the role `admin`.
3 | The role policy `role-policy1` allows only authenticated requests to access `/crud/modify` and `/crud/id` sub-paths. For more information about the path-matching algorithm, see Matching multiple paths: longest path wins later in this guide.
4 | You can specify a custom implementation of the `java.security.Permission` class. Your custom class must define exactly one constructor that accepts the permission name and optionally some actions, for example, `String` array. In this scenario, the permission `list` is added to the `SecurityIdentity` instance as `new CustomPermission("list")`.

You can also create a custom `java.security.Permission` class with additional constructor parameters. These additional parameters names get matched with arguments names of the method annotated with the `@PermissionsAllowed` annotation. Later, Quarkus instantiates your custom permission with actual arguments, with which the method annotated with the `@PermissionsAllowed` has been invoked.

Example of a custom `java.security.Permission` class that accepts additional arguments

    package org.acme.library;

    import java.security.Permission;
    import java.util.Arrays;
    import java.util.Set;

    public class LibraryPermission extends Permission {

        private final Set<String> actions;
        private final Library library;

        public LibraryPermission(String libraryName, String[] actions, Library library) { (1)
            super(libraryName);
            this.actions = Set.copyOf(Arrays.asList(actions));
            this.library = library;
        }

        @Override
        public boolean implies(Permission requiredPermission) {
            if (requiredPermission instanceof LibraryPermission) {
                LibraryPermission that = (LibraryPermission) requiredPermission;
                boolean librariesMatch = getName().equals(that.getName());
                boolean requiredLibraryIsSublibrary = library.isParentLibraryOf(that.library);
                boolean hasOneOfRequiredActions = that.actions.stream().anyMatch(actions::contains);
                return (librariesMatch || requiredLibraryIsSublibrary) && hasOneOfRequiredActions;
            }
            return false;
        }

        // here comes your own implementation of the `java.security.Permission` class methods

        public static abstract class Library {

            protected String description;

            abstract boolean isParentLibraryOf(Library library);

        }

        public static class MediaLibrary extends Library {

            @Override
            boolean isParentLibraryOf(Library library) {
                return library instanceof MediaLibrary;
            }
        }

        public static class TvLibrary extends MediaLibrary {
            // TvLibrary specific implementation of the 'isParentLibraryOf' method
        }
    }

1 | There must be exactly one constructor of a custom `Permission` class. The first parameter is always considered to be a permission name and must be of type `String`. Quarkus can optionally pass permission actions to the constructor. For this to happen, declare the second parameter as `String[]`.

The `LibraryPermission` class permits access to the current or parent library if `SecurityIdentity` is allowed to perform one of the required actions, for example, `read`, `write`, or `list`.

The following example shows how the `LibraryPermission` class can be used:

    package org.acme.library;

    import io.quarkus.security.PermissionsAllowed;
    import jakarta.enterprise.context.ApplicationScoped;
    import org.acme.library.LibraryPermission.Library;

    @ApplicationScoped
    public class LibraryService {

        @PermissionsAllowed(value = "tv:write", permission = LibraryPermission.class) (1)
        public Library updateLibrary(String newDesc, Library library) {
            library.description = newDesc;
            return library;
        }

        @PermissionsAllowed(value = "tv:write", permission = LibraryPermission.class) (2)
        @PermissionsAllowed(value = {"tv:read", "tv:list"}, permission = LibraryPermission.class)
        public Library migrateLibrary(Library migrate, Library library) {
            // migrate libraries
            return library;
        }

    }

1 | The formal parameter `library` is identified as the parameter matching same-named `LibraryPermission` constructor parameter. Therefore, Quarkus will pass the `library` parameter to the `LibraryPermission` class constructor. However, the `LibraryPermission` must be instantiated each time the `updateLibrary` method is invoked.
2 | Here, the second `Library` parameter matches the name `library`, while the `migrate` parameter is ignored during the `LibraryPermission` permission instantiation.

Example of a resource secured with the `LibraryPermission`

    package org.acme.library;

    import io.quarkus.security.PermissionsAllowed;
    import jakarta.inject.Inject;
    import jakarta.ws.rs.PUT;
    import jakarta.ws.rs.Path;
    import jakarta.ws.rs.PathParam;
    import org.acme.library.LibraryPermission.Library;

    @Path("/library")
    public class LibraryResource {

        @Inject
        LibraryService libraryService;

        @PermissionsAllowed(value = "tv:write", permission = LibraryPermission.class)
        @PUT
        @Path("/id/{id}")
        public Library updateLibrary(@PathParam("id") Integer id, Library library) {
            ...
        }

        @PUT
        @Path("/service-way/id/{id}")
        public Library updateLibrarySvc(@PathParam("id") Integer id, Library library) {
            String newDescription = "new description " + id;
            return libraryService.updateLibrary(newDescription, library);
        }

    }

Similarly to the `CRUDResource` example, the following example shows how you can grant a user with the `admin` role permissions to update `MediaLibrary`:

    package org.acme.library;

    import io.quarkus.runtime.annotations.RegisterForReflection;

    @RegisterForReflection (1)
    public class MediaLibraryPermission extends LibraryPermission {

        public MediaLibraryPermission(String libraryName, String[] actions) {
            super(libraryName, actions, new MediaLibrary());    (2)
        }

    }

1 | When building a native executable, the permission class must be registered for reflection unless it is also used in at least one `io.quarkus.security.PermissionsAllowed#name` parameter. More details about the `@RegisterForReflection` annotation can be found on the native application tips page.
2 | We want to pass the `MediaLibrary` instance to the `LibraryPermission` constructor.

    quarkus.http.auth.policy.role-policy3.permissions.admin=media-library:list,media-library:read,media-library:write   (1)
    quarkus.http.auth.policy.role-policy3.permission-class=org.acme.library.MediaLibraryPermission
    quarkus.http.auth.permission.roles3.paths=/library/*
    quarkus.http.auth.permission.roles3.policy=role-policy3

1 | Grants the permission `media-library`, which permits `read`, `write`, and `list` actions. Because `MediaLibrary` is the `TvLibrary` class parent, a user with the `admin` role is also permitted to modify `TvLibrary`.

| The `/library/*` path can be tested from a Keycloak provider Dev UI page, because the user `alice` which is created automatically by the Dev Services for Keycloak has an `admin` role.

The examples provided so far demonstrate role-to-permission mapping. It is also possible to programmatically add permissions to the `SecurityIdentity` instance. In the following example, `SecurityIdentity` is customized to add the same permission that was previously granted with the HTTP role-based policy.

Example of adding the `LibraryPermission` programmatically to `SecurityIdentity`

    import java.security.Permission;

    import jakarta.enterprise.context.ApplicationScoped;

    import io.quarkus.security.identity.AuthenticationRequestContext;
    import io.quarkus.security.identity.SecurityIdentity;
    import io.quarkus.security.identity.SecurityIdentityAugmentor;
    import io.quarkus.security.runtime.QuarkusSecurityIdentity;
    import io.smallrye.mutiny.Uni;

    @ApplicationScoped
    public class PermissionsIdentityAugmentor implements SecurityIdentityAugmentor {

        @Override
        public Uni<SecurityIdentity> augment(SecurityIdentity identity, AuthenticationRequestContext context) {
            if (isNotAdmin(identity)) {
                return Uni.createFrom().item(identity);
            }
            return Uni.createFrom().item(build(identity));
        }

        private boolean isNotAdmin(SecurityIdentity identity) {
            return identity.isAnonymous() || !"admin".equals(identity.getPrincipal().getName());
        }

        SecurityIdentity build(SecurityIdentity identity) {
            return QuarkusSecurityIdentity.builder(identity)
                    .addPermission(new MediaLibraryPermission("media-library", new String[] { "read", "write", "list"}); (1)
                    .build();
        }

    }

1 | Add a `media-library` permission that was created can perform `read`, `write`, and `list` actions. Because `MediaLibrary` is the `TvLibrary` class parent, a user with the `admin` role is also permitted to modify `TvLibrary`.

| Annotation-based permissions do not work with custom Jakarta REST SecurityContexts because there are no permissions in `jakarta.ws.rs.core.SecurityContext`.

#### Create permission checkers

By default, `SecurityIdentity` must be configured with permissions which can be used to check if this identity passes `@PermissionAllowed` authorization restrictions. Alternatively, you can use a `@PermissionChecker` annotation to mark any CDI bean method as a permission checker. The `@PermissionChecker` annotation value should match required permission declared by the `@PermissionsAllowed` annotation value. For example, a permission checker can be created like this:

    package org.acme.security.rest.resource;

    import io.quarkus.security.PermissionChecker;
    import io.quarkus.security.PermissionsAllowed;
    import io.quarkus.security.identity.SecurityIdentity;
    import jakarta.ws.rs.POST;
    import jakarta.ws.rs.Path;

    import org.jboss.resteasy.reactive.RestForm;
    import org.jboss.resteasy.reactive.RestPath;

    @Path("/project/{projectName}")
    public class ProjectResource {

        @PermissionsAllowed("rename-project") (1)
        @POST
        public void renameProject(@RestPath String projectName, @RestForm String newName) {
            Project project = Project.findByName(projectName);
            project.name = newName;
        }

        @PermissionChecker("rename-project") (2)
        boolean canRenameProject(SecurityIdentity identity, String projectName) {  (3) (4)
            var principalName = identity.getPrincipal().getName();
            var user = User.getUserByName(principalName);
            return userOwnsProject(projectName, user);
        }
    }

1 | The permission required to access the `ProjectResource#renameProject` is the `rename-project` permission.
2 | The `ProjectResource#canRenameProject` method authorizes access to the `ProjectResource#renameProject` endpoint.
3 | The `SecurityIdentity` instance can be injected into any permission checker method.
4 | In this example, the `rename-project` permission checker is declared on the same resource. However, there is no restriction on where this permission checker can be declared as demonstrated in the next example.

| Permission checker methods can be declared on a normal scoped CDI bean or on a `@Singleton` bean. The `@Dependent` CDI bean scope is currently not supported.

The permission checker above requires the `SecurityIdentity` instance to authorize the `renameProject` endpoint. Instead of declaring the `rename-project` permission checker directly on the resource, you can declare it on any CDI bean like in this example:

    package org.acme.security.rest.resource;

    import io.quarkus.security.PermissionChecker;
    import jakarta.enterprise.context.ApplicationScoped;
    import jakarta.inject.Inject;

    @ApplicationScoped (1)
    public class ProjectPermissionChecker {

        @PermissionChecker("rename-project")
        boolean canRenameProject(String projectName, SecurityIdentity identity) {   (2)
            var principalName = identity.getPrincipal().getName();
            var user = User.getUserByName(principalName);
            return userOwnsProject(projectName, user);
        }

    }

1 | A CDI bean with the permission checker must be either a normal scoped bean or a `@Singleton` bean.
2 | The permission checker method must return either `boolean` or `Uni<Boolean>`. Private checker methods are not supported.

| Permission checks run by default on event loops. Annotate a permission checker method with the `io.smallrye.common.annotation.Blocking` annotation if you want to run the check on a worker thread.

Matching between the `@PermissionsAllowed` values and the `@PermissionChecker` value is based on string equality as shown in the example below:

    package org.acme.security;

    import io.quarkus.security.PermissionChecker;
    import io.quarkus.security.PermissionsAllowed;
    import jakarta.enterprise.context.ApplicationScoped;

    @ApplicationScoped
    public class FileService {

        @PermissionsAllowed({ "delete:all", "delete:dir" }) (1)
        void deleteDirectory(Path directoryPath) {
            // delete directory
        }

        @PermissionsAllowed(value = { "delete:service", "delete:file" }, inclusive = true) (2)
        void deleteServiceFile(Path serviceFilePath) {
            // delete service file
        }

        @PermissionChecker("delete:all")
        boolean canDeleteAllDirectories(SecurityIdentity identity) {
            String filePermissions = identity.getAttribute("user-group-file-permissions");
            return filePermissions != null && filePermissions.contains("w");
        }

        @PermissionChecker("delete:service")
        boolean canDeleteService(SecurityIdentity identity) {
            return identity.hasRole("admin");
        }

        @PermissionChecker("delete:file")
        boolean canDeleteFile(Path serviceFilePath) {
            return serviceFilePath != null && !serviceFilePath.endsWith("critical");
        }
    }

1 | The permission checker method `canDeleteAllDirectories` grants access to the `deleteDirectory` because the `delete:all` values are equal.
2 | There must be exactly two permission checker methods, one for the `delete:service` permission and other for the `delete:file` permission.

#### Create permission meta-annotations

`@PermissionsAllowed` can also be used in meta-annotations. For example, a new `@CanWrite` security annotation can be created like this:

    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;

    import io.quarkus.security.PermissionsAllowed;

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ ElementType.METHOD, ElementType.TYPE })
    @PermissionsAllowed(value = "write", permission = CustomPermission.class) (1)
    public @interface CanWrite {

    }

1 | Any method or class annotated with the `@CanWrite` annotation is secured with this `@PermissionsAllowed` annotation instance.

#### Pass `@BeanParam` parameters into a custom permission

Quarkus can map fields of a secured method parameters to a custom permission constructor parameters. You can use this feature to pass `jakarta.ws.rs.BeanParam` parameters into your custom permission. Let’s consider following Jakarta REST resource:

    package org.acme.security.rest.resource;

    import io.quarkus.security.PermissionsAllowed;
    import jakarta.ws.rs.BeanParam;
    import jakarta.ws.rs.GET;
    import jakarta.ws.rs.Path;

    @Path("/hello")
    public class HelloResource {

        @PermissionsAllowed(value = "say-hello", params = "beanParam.securityContext.userPrincipal.name") (1)
        @GET
        public String sayHello(@BeanParam SimpleBeanParam beanParam) {
            return "Hello from " + beanParam.uriInfo.getPath();
        }

    }

1 | The `params` annotation attribute specifies that user principal name should be passed to the `BeanParamPermissionChecker#canSayHello` method. Other `BeanParamPermissionChecker#canSayHello` method parameters like `customAuthorizationHeader` and `query` are matched automatically. Quarkus identifies the `BeanParamPermissionChecker#canSayHello` method parameters among `beanParam` fields and their public accessors. To avoid ambiguous resolution, automatic detection only works for the `beanParam` fields. For that reason, we had to specify path to the user principal name explicitly.

Where the `SimpleBeanParam` class is declared like in the example below:

    package org.acme.security.rest.dto;

    import java.util.List;

    import jakarta.ws.rs.HeaderParam;
    import jakarta.ws.rs.QueryParam;
    import jakarta.ws.rs.core.Context;
    import jakarta.ws.rs.core.SecurityContext;
    import jakarta.ws.rs.core.UriInfo;

    public class SimpleBeanParam {

        @HeaderParam("CustomAuthorization")
        private String customAuthorizationHeader;

        @Context
        SecurityContext securityContext;

        @Context
        public UriInfo uriInfo;

        @QueryParam("query")
        public String query;    (1)

        public SecurityContext getSecurityContext() { (2)
            return securityContext;
        }

        public String customAuthorizationHeader() { (3)
            return customAuthorizationHeader;
        }
    }

1 | Quarkus Security can only pass public fields to a custom permission constructor.
2 | Quarkus Security automatically uses public getter methods if they are available.
3 | The `customAuthorizationHeader` field is not public, therefore Quarkus access this field with the `customAuthorizationHeader` accessor. That is particularly useful with Java records, where generated accessors are not prefixed with `get`.

Here is an example of a `@PermissionChecker` method that checks the `say-hello` permission based on a user principal, custom header and query parameter:

    package org.acme.security.permission;

    import jakarta.enterprise.context.ApplicationScoped;
    import io.quarkus.security.PermissionChecker;

    @ApplicationScoped
    public class BeanParamPermissionChecker {

        @PermissionChecker("say-hello")
        boolean canSayHello(String customAuthorizationHeader, String name, String query) {
            boolean queryParamAllowedForPermissionName = checkQueryParams(query);
            boolean usernameWhitelisted = isUserNameWhitelisted(name);
            boolean customAuthorizationMatches = checkCustomAuthorization(customAuthorizationHeader);
            return queryParamAllowedForPermissionName && usernameWhitelisted && customAuthorizationMatches;
        }

        ...
    }

| You can pass `@BeanParam` directly into a `@PermissionChecker` method and access its fields programmatically. Ability to reference `@BeanParam` fields with the `@PermissionsAllowed#params` attribute is useful when you have multiple differently structured `@BeanParam` classes.

## References

- Quarkus Security overview

- Quarkus Security architecture

- Authentication mechanisms in Quarkus

- Basic authentication

- Getting started with Security by using Basic authentication and Jakarta Persistence

- OpenID Connect Bearer Token Scopes And SecurityIdentity Permissions