Giter VIP home page Giter VIP logo

micronaut-security's Introduction

Micronaut Security

Maven Central Snapshot Build Status Quality Gate Status Revved up by Develocity

The official Micronaut security solution.

Known Vulnerabilities (by Snyk Snyk)

Known Vulnerabilities Known Vulnerabilities Known Vulnerabilities Known Vulnerabilities Known Vulnerabilities

Documentation

See the Documentation for more information.

Snapshot Documentation.

Build and Run tests Locally

./gradlew -Dgeb.env=firefox build

micronaut-security's People

Contributors

abedurftig avatar alvarosanchez avatar aruld avatar benrhine avatar crazysmoove avatar ctoestreich avatar dependabot-preview[bot] avatar dependabot[bot] avatar graemerocher avatar ilopmar avatar jagedn avatar jameskleeh avatar jcassee avatar jeremyg484 avatar marceloverdijk avatar mattmoss avatar micronaut-build avatar morki avatar niravassar avatar puneetbehl avatar remkop avatar renovate[bot] avatar rvanderwerf avatar sascha-frinken avatar saw303 avatar sdelamo avatar timyates avatar wetted avatar zacharyklein avatar zendern avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

micronaut-security's Issues

@PermitAll annotation currently not working

While securing some endpoints I noticed that due to the implementation of the SecuredAnnotationRule within the following code snippet, the @permitAll annotation will yield a 403 Forbidden response:

@Override public SecurityRuleResult check(HttpRequest request, @Nullable RouteMatch routeMatch, @Nullable Map<String, Object> claims) {
    if (routeMatch instanceof MethodBasedRouteMatch) {
        MethodBasedRouteMatch methodRoute = ((MethodBasedRouteMatch) routeMatch);
        if (methodRoute.hasAnnotation(Secured.class)) {
            Optional<String[]> optionalValue = methodRoute.getValue(Secured.class, String[].class);
            if (optionalValue.isPresent()) {
                List<String> values = Arrays.asList(optionalValue.get());
                if (values.contains(SecurityRule.DENY_ALL)) {
                    return SecurityRuleResult.REJECTED;
                }
                return compareRoles(values, getRoles(claims));
            }
        }
    }
    return SecurityRuleResult.UNKNOWN;
}

The guide available at https://micronaut-projects.github.io/micronaut-security/latest/guide/ denotes that use of these JSR-250 (https://jcp.org/en/jsr/detail?id=250) annotations should be working, so either this functionality should be updated, or the documentation should be removed.

custom SecurityRule with multitenancy

use multitenancy and configuration:micronaut-multitenancy-gorm

compile 'io.micronaut:micronaut-multitenancy'
compile "io.micronaut.configuration:micronaut-multitenancy-gorm"

i my custom

class ZysoftSecuredAnnotationRule implements SecurityRule {

    @Inject
    TenantResolver tenantResolver
}

then
tenantResolver.resolveTenantIdentifier() throw exception
Tenant could not be resolved outside a web request

i just create a demo project

https://github.com/ibmsoft/-micronaut-test-securityrule

Allow 404 global error to be displayed even with a security enabled

Steps to Reproduce

  1. Add a global 404 error @Error(status = HttpStatus.NOT_FOUND, global = true)
  2. Enable security with micronaut.security.enabled: true
  3. Add a controller with parameter like
@Secured(Role.ROLE_CONNECT_READ)
@Controller("/{cluster}/connect")
public class ConnectController {
  1. Try to reach every child page like /test/connect/bla
  2. be redirected to unauthorized url
  3. Disabled security with micronaut.security.enabled: false
  4. Try to reach the same page like /test/connect/bla
  5. see the 404 page

Expected Behaviour

Security should not change the behavior off 404 page and must be check before security for some use case.
The best will be to let user have an option to allow choose if you you want to have a 404 or a unauthorized response.

I think the better option is to allow 404 before unauthorized if the ErrorController is annotated with : @Secured(SecurityRule.IS_ANONYMOUS)

Environment Information

  • Operating System: Docker alpine
  • Micronaut Version: 1.1.0
  • JDK Version: openjdk:8-jre-alpine

Example Application

Full source code is here on branch dev

Limit access to service based on IP only

Is it possible to setup access to service based on IP only (using ip-patterns), without setting up any security providers?

Given the tests below, if it passes the ip-patterns, it is still failing with No rule provider authorized or rejected the request.

If it fails the ip-patterns, you get The rule provider io.micronaut.security.rules.IpPatternsRule rejected the request.

So based on the tests below, i know the ip-pattern rule is working. Are there any other configurations needed to just protect the endpoint based the the client IP?

Looking at the IpPattern code, when it passes the ip-pattern rule, it seem to always to return SecurityRuleResult.UNKNOWN

    public SecurityRuleResult check(HttpRequest request, @Nullable RouteMatch routeMatch, @Nullable Map<String, Object> claims) {

        if (patternList.isEmpty()) {
            return SecurityRuleResult.UNKNOWN;
        } else {
            if (patternList.stream().anyMatch(pattern ->
                    pattern.pattern().equals(SecurityConfigurationProperties.ANYWHERE) ||
                    pattern.matcher(request.getRemoteAddress().getAddress().getHostAddress()).matches())) {
                return SecurityRuleResult.UNKNOWN;
            } else {
                return SecurityRuleResult.REJECTED;
            }
        }
    }

Test 1:

Secenario: Testing from 127.0.0.1, results in log entries below.

micronaut:
  application:
    name: test-app
  security:
    enabled: true
    ip-patterns:
      - '127.0.0.1'

15:41:37.320 [pool-1-thread-7] DEBUG i.m.security.filters.SecurityFilter - Failure to authenticate request. GET /test.
15:41:37.324 [pool-1-thread-7] DEBUG i.m.security.filters.SecurityFilter - Authorized request GET /test. No rule provider authorized or rejected the request.

Test 2:

Secenario: Testing from 127.0.0.1, results in log entries below.

micronaut:
  application:
    name: test-app
  security:
    enabled: true
    ip-patterns:
      - '198.1.1.1'

15:46:58.376 [pool-1-thread-4] DEBUG i.m.security.filters.SecurityFilter - Failure to authenticate request. GET /test.
15:46:58.381 [pool-1-thread-4] DEBUG i.m.security.filters.SecurityFilter - Unauthorized request GET /test. The rule provider io.micronaut.security.rules.IpPatternsRule rejected the request.

Can't inject SecurityService inside of an Around Advice

I have a custom annotation "Managed" that wants to check for the SecurityService to perform some extra logic.

Here is the source of the annotation:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Around
@Type(ManagedAdvice.class)
public @interface Managed {
}

The advice that is executed looks like this:

@Singleton
@Requires(classes = SecurityService.class)
public class ManagedAdvice implements MethodInterceptor<Object, Object> {
	@Inject
	SecurityService securityService;

	@Inject
	Connection natsConnection;

	@Override
	public Object intercept(MethodInvocationContext<Object, Object> context) {
	// ...

and example usage of this annotation is as follows:

	@Managed
	@View("callboard/home")
	@Get("/")
	public HttpResponse home() {
	// ...

Anyone has a clue as to why this bean would not be found during runtime?

Here is the stack-trace for the error-message:

io.micronaut.context.exceptions.DependencyInjectionException: Failed to inject value for field [securityService] of class: customProject.advice.ManagedAdvice

Path Taken: new $CallboardControllerDefinition$Intercepted(BeanContext beanContext,[Interceptor[] interceptors]) --> ManagedAdvice.securityService
	at io.micronaut.context.AbstractBeanDefinition.getBeanForField(AbstractBeanDefinition.java:1354)
	at io.micronaut.context.AbstractBeanDefinition.getBeanForField(AbstractBeanDefinition.java:1148)
	at customProject.advice.$ManagedAdviceDefinition.injectBean(Unknown Source)
	at customProject.advice.$ManagedAdviceDefinition.build(Unknown Source)
	at io.micronaut.context.DefaultBeanContext.doCreateBean(DefaultBeanContext.java:1494)
	at io.micronaut.context.DefaultBeanContext.addCandidateToList(DefaultBeanContext.java:2495)
	at io.micronaut.context.DefaultBeanContext.getBeansOfTypeInternal(DefaultBeanContext.java:2395)
	at io.micronaut.context.DefaultBeanContext.getBeansOfType(DefaultBeanContext.java:854)
	at io.micronaut.context.AbstractBeanDefinition.lambda$getBeansOfTypeForConstructorArgument$10(AbstractBeanDefinition.java:1088)
	at io.micronaut.context.AbstractBeanDefinition.resolveBeanWithGenericsFromConstructorArgument(AbstractBeanDefinition.java:1697)
	at io.micronaut.context.AbstractBeanDefinition.getBeansOfTypeForConstructorArgument(AbstractBeanDefinition.java:1083)
	at io.micronaut.context.AbstractBeanDefinition.getBeanForConstructorArgument(AbstractBeanDefinition.java:959)
	at customProject.controllers.callboard.$$CallboardControllerDefinition$InterceptedDefinition.build(Unknown Source)
	at io.micronaut.context.DefaultBeanContext.doCreateBean(DefaultBeanContext.java:1494)
	at io.micronaut.context.DefaultBeanContext.createAndRegisterSingleton(DefaultBeanContext.java:2163)
	at io.micronaut.context.DefaultBeanContext.getBeanForDefinition(DefaultBeanContext.java:1849)
	at io.micronaut.context.DefaultBeanContext.getBeanInternal(DefaultBeanContext.java:1829)
	at io.micronaut.context.DefaultBeanContext.getBean(DefaultBeanContext.java:577)
	at io.micronaut.context.DefaultBeanContext$BeanExecutionHandle.getTarget(DefaultBeanContext.java:2681)
	at io.micronaut.context.DefaultBeanContext$BeanExecutionHandle.invoke(DefaultBeanContext.java:2702)
	at io.micronaut.web.router.AbstractRouteMatch.execute(AbstractRouteMatch.java:236)
	at io.micronaut.web.router.RouteMatch.execute(RouteMatch.java:122)
	at io.micronaut.http.server.netty.RoutingInBoundHandler.lambda$buildResultEmitter$17(RoutingInBoundHandler.java:1360)
	at io.reactivex.internal.operators.flowable.FlowableCreate.subscribeActual(FlowableCreate.java:71)
	at io.reactivex.Flowable.subscribe(Flowable.java:14805)
	at io.reactivex.Flowable.subscribe(Flowable.java:14752)
	at io.micronaut.reactive.rxjava2.RxInstrumentedFlowable.subscribeActual(RxInstrumentedFlowable.java:68)
	at io.reactivex.Flowable.subscribe(Flowable.java:14805)
	at io.reactivex.internal.operators.flowable.FlowableMap.subscribeActual(FlowableMap.java:37)
	at io.reactivex.Flowable.subscribe(Flowable.java:14805)
	at io.reactivex.Flowable.subscribe(Flowable.java:14752)
	at io.micronaut.reactive.rxjava2.RxInstrumentedFlowable.subscribeActual(RxInstrumentedFlowable.java:68)
	at io.reactivex.Flowable.subscribe(Flowable.java:14805)
	at io.reactivex.internal.operators.flowable.FlowableSwitchIfEmpty.subscribeActual(FlowableSwitchIfEmpty.java:32)
	at io.reactivex.Flowable.subscribe(Flowable.java:14805)
	at io.reactivex.Flowable.subscribe(Flowable.java:14752)
	at io.micronaut.reactive.rxjava2.RxInstrumentedFlowable.subscribeActual(RxInstrumentedFlowable.java:68)
	at io.reactivex.Flowable.subscribe(Flowable.java:14805)
	at io.reactivex.Flowable.subscribe(Flowable.java:14755)
	at io.micronaut.http.context.ServerRequestTracingPublisher.lambda$subscribe$0(ServerRequestTracingPublisher.java:52)
	at io.micronaut.http.context.ServerRequestContext.with(ServerRequestContext.java:52)
	at io.micronaut.http.context.ServerRequestTracingPublisher.subscribe(ServerRequestTracingPublisher.java:52)
	at io.reactivex.internal.operators.flowable.FlowableFromPublisher.subscribeActual(FlowableFromPublisher.java:29)
	at io.reactivex.Flowable.subscribe(Flowable.java:14805)
	at io.reactivex.Flowable.subscribe(Flowable.java:14752)
	at io.micronaut.reactive.rxjava2.RxInstrumentedFlowable.subscribeActual(RxInstrumentedFlowable.java:68)
	at io.reactivex.Flowable.subscribe(Flowable.java:14805)
	at io.reactivex.internal.operators.flowable.FlowableSwitchMap.subscribeActual(FlowableSwitchMap.java:49)
	at io.reactivex.Flowable.subscribe(Flowable.java:14805)
	at io.reactivex.Flowable.subscribe(Flowable.java:14752)
	at io.micronaut.reactive.rxjava2.RxInstrumentedFlowable.subscribeActual(RxInstrumentedFlowable.java:68)
	at io.reactivex.Flowable.subscribe(Flowable.java:14805)
	at io.reactivex.Flowable.subscribe(Flowable.java:14752)
	at io.reactivex.internal.operators.flowable.FlowableSubscribeOn$SubscribeOnSubscriber.run(FlowableSubscribeOn.java:82)
	at io.reactivex.internal.schedulers.ExecutorScheduler$ExecutorWorker$BooleanRunnable.run(ExecutorScheduler.java:288)
	at io.reactivex.internal.schedulers.ExecutorScheduler$ExecutorWorker.run(ExecutorScheduler.java:253)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
	at java.lang.Thread.run(Unknown Source)
Caused by: io.micronaut.context.exceptions.NoSuchBeanException: No bean of type [io.micronaut.security.utils.SecurityService] exists. Make sure the bean is not disabled by bean requirements (enable trace logging for 'io.micronaut.context.condition' to check) and if the bean is enabled then ensure the class is declared a bean and annotation processing is enabled (for Java and Kotlin the 'micronaut-inject-java' dependency should be configured as an annotation processor).
	at io.micronaut.context.DefaultBeanContext.getBeanInternal(DefaultBeanContext.java:1835)
	at io.micronaut.context.DefaultBeanContext.getBean(DefaultBeanContext.java:997)
	at io.micronaut.context.AbstractBeanDefinition.getBeanForField(AbstractBeanDefinition.java:1346)
	... 58 common frames omitted

Add ldap read timeout in the application.yml

may I ask if the lap server read timeout can be configured in the application.yml? I found a workaround way is to create my own DefaultContextBuilder as follow and add the explicit timeout value

@Singleton
@Replaces(DefaultContextBuilder.class)
public class MyDefaultContextBuilder implements ContextBuilder {

        private static final Logger LOG = LoggerFactory.getLogger(RglDefaultContextBuilder.class);

        @Override
        public DirContext build(ContextSettings contextSettings) throws NamingException {
                return build(
                        contextSettings.getFactory(),
                        contextSettings.getUrl(),
                        contextSettings.getDn(),
                        contextSettings.getPassword(),
                        contextSettings.getPooled());
        }

        @Override
        public DirContext build(String factory, String server, String user, String password, boolean pooled) throws NamingException {
                Properties props = new Properties();
                props.put(Context.INITIAL_CONTEXT_FACTORY, factory);
                props.put(Context.PROVIDER_URL, server);
                props.put(Context.SECURITY_AUTHENTICATION, "simple");
                props.put(Context.SECURITY_PRINCIPAL, user);
                props.put(Context.SECURITY_CREDENTIALS, password);

                // Override the lap timeout value explicitly for 5 seconds
                props.put("com.sun.jndi.ldap.read.timeout", "5");
                if (pooled) {
                        props.put("com.sun.jndi.ldap.connect.pool", "true");
                }

                return new InitialDirContext(props);
        }
}

Throw an error when 2 or more security annotations are applied to a method

Discovered this during @sdelamo deep dive presentation. If you add @Secured and @RolesAllowed, the method will use the first annotation found. Instead, it should throw an error to let the developer know that only one annotation is supported.

Example:

    @Secured(SecurityRule.IS_AUTHENTICATED)
    @View("books")
    @Get
    @RolesAllowed("ROLE_USER")
    public Map<String, Object> index() {
        Map<String, Object> model = new HashMap<>();
        model.put("books", Arrays.asList(new Book("1491950358", "Building Microservices"),
        new Book("1680502395", "Release It!"),
        new Book("0321601912", "Continuous Delivery")));
        return model;
    }

Kerberos support

I want to integrate my Micronaut app with Kerberos Server (using Kerberos authentication protocol). Users should be authenticated against app by opening the URL.
User should not enter a username/password and install additional software.

Are there any activities planned in this topic?

Redis as a sessions store does not work

Enabling Redis as a session cache in Micronaut does not work.

Using the code from the session guide as a starting point:

https://guides.micronaut.io/micronaut-security-session/guide/index.html

Add the following to the appplication.yml:

session:
    http:
      redis:
        enabled: true
        write-mode: BACKGROUND
        value-serializer: io.micronaut.core.serialize.JdkSerializer

redis:
  uri: redis://localhost
  ssl: false

Using the io.micronaut.core.serialize.JdkSerializer you will the following exception when trying to login:

Caused by: io.micronaut.core.serialize.exceptions.SerializationException: I/O error occurred during serialization: io.micronaut.security.authentication.AuthenticationUserDetailsAdapter
        at io.micronaut.core.serialize.JdkSerializer.serialize(JdkSerializer.java:62)
        at io.micronaut.core.serialize.ObjectSerializer.serialize(ObjectSerializer.java:74)
        at io.micronaut.configuration.lettuce.session.RedisSessionStore$RedisSession.convertAttribute(RedisSessionStore.java:726)
        at io.micronaut.configuration.lettuce.session.RedisSessionStore$RedisSession.delta(RedisSessionStore.java:683)
        at io.micronaut.configuration.lettuce.session.RedisSessionStore.save(RedisSessionStore.java:279)
        at io.micronaut.configuration.lettuce.session.RedisSessionStore.save(RedisSessionStore.java:84)
        at io.micronaut.session.http.HttpSessionFilter.lambda$null$4(HttpSessionFilter.java:134)
        at io.micronaut.core.async.publisher.CompletableFuturePublisher$CompletableFutureSubscription.request(CompletableFuturePublisher.java:75)
        at io.reactivex.internal.subscribers.BasicFuseableSubscriber.request(BasicFuseableSubscriber.java:153)
        at io.reactivex.internal.operators.flowable.FlowableSwitchMap$SwitchMapInnerSubscriber.onSubscribe(FlowableSwitchMap.java:379)
        at io.micronaut.reactive.rxjava2.InstrumentedSubscriber.onSubscribe(InstrumentedSubscriber.java:75)
        at io.reactivex.internal.subscribers.BasicFuseableSubscriber.onSubscribe(BasicFuseableSubscriber.java:67)
        at io.micronaut.reactive.rxjava2.InstrumentedSubscriber.onSubscribe(InstrumentedSubscriber.java:75)
        at io.micronaut.core.async.publisher.CompletableFuturePublisher.subscribe(CompletableFuturePublisher.java:48)
        at io.reactivex.internal.operators.flowable.FlowableFromPublisher.subscribeActual(FlowableFromPublisher.java:29)
        at io.reactivex.Flowable.subscribe(Flowable.java:14805)
        ... 250 common frames omitted
Caused by: java.io.NotSerializableException: io.micronaut.security.authentication.AuthenticationUserDetailsAdapter
        at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
        at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
        at io.micronaut.core.serialize.JdkSerializer.serialize(JdkSerializer.java:58)
        ... 265 common frames omitted

Switching the serializer to: io.micronaut.jackson.serialize.JacksonObjectSerializer causes a different issue. When logging in you will be redirected to the login success page but it will state you aren't logged in despite the session cookie being set.

The issue here seems to be that RedisSession uses strings for the keys in it's atributeMap during construction but methods such as SessionAuthenticationFetcher.fetchAuthentication are using an object as the key when they try to look up the authentication object.

In this case: SecurityFilter.AUTHENTICATION is being converted to "micronaut.AUTHENTICATION" and so the authentication object is not found.

X509 Certificate Authentication

Feature request

It would be great if Micronaut had this out of the box with clear instructions on setting up the necessary certificate stores server-side. Especially the procedure for creating the certificate stores and configuration for complicated PKI like the US DoD's CAC cards, which have 5 root CA's and dozens of intermediate ones.

Feature Request: Support Client Credentials Grant

I see a lot of documentation, but none detailing how to authenticate via OAuth2 in HTTP client.
Is this planned in this library?

Or is it already implement and I just didn't find it?
Or is it already supported by existing micronaut library?

Refactor AuthenticationResponse

Currently the login controller makes assumptions that a successful authentication response is a UserDetails and a failed response is an AuthenticationFailed. We should remove that assumption and change the response interface to allow for handling of both types of responses. UserDetails should be an implementation detail instead of a requirement

can't use oauth2 micronaut with snapshot reposistory

I try to test oauth2 micronaut :

But I can not include security-oauth2 by including in gradle dependencies :

compile "io.micronaut.configuration:security-oauth2:1.2.0.BUILD-SNAPSHOT"

or
compile "io.micronaut:security-oauth2:1.2.0.BUILD-SNAPSHOT"

with repository :
repositories {
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
maven { url "https://jcenter.bintray.com" }
jcenter()
}

it's working with

annotationProcessor "io.micronaut:micronaut-security:1.2.0.BUILD-SNAPSHOT"
compile "io.micronaut:micronaut-security:1.2.0.BUILD-SNAPSHOT"

but not for oauth2... how can I do that ?

Resource access based roles with @Secured annotation

Hello, I'm trying to get a micronaut microservice working with OpenID Connect.
I managed to get the basic setup working eventually (with help of micronaut-oauth2).

But when trying to set up role-based authentication via @Secured, I noticed that the default SecuredAnnotationRule cannot parse roles from the JWT generated by Keycloak. It is able to get list of roles from an Iterable, but the format is:

{
  "realm_access": {
    "roles": [
      "global_role_1"
    ]
  },
  "resource_access": {
    "resource_1": {
      "roles": [
        "resource_specific_role_1"
      ]
    }
  }
}

Unfortunately, I cannot (simply) extend the SecuredAnnotationRule nor the the AbstractSecurityRule as their constructors are package-private.

Do you plan to support resource-based authorization?

Should I create an issue for this in micronaut-core?

Java example for OpenID config

Please add Java example how to add OpenID config. I saw there is a Groovy one, but unfortunately I'm not that familiar with it. I think the same for many others.

Allow setting or strategy to indicate all authentication providers must authenticate successfully

I created an example project:
https://github.com/andcuevas/micronaut-authentication-provider-rejection

There are two authentication providers:

  1. FailedAuthenticationProvider
  2. SuccesfulAuthenticationProvider

As the names indicate, one authenticates the user and the other doesn't.
If you run the test class micronaut.custom.authentication.FailedAuthProviderTest, the user is authenticated.

The result is the same if FailedAuthenticationProvider.authenticate() returns:

  1. Flowable.just(new AuthenticationFailed());
  2. Flowable.empty();
  3. Flowable.error(new Exception());

I would like to have the possibility to reject the user if one authenticator says that the authentication is invalid.

ServerRequestMeterRegistryFilter is not invoked in case of 401

Task List

  • Steps to reproduce provided
  • Stacktrace (if present) provided
  • Example that reproduces the problem uploaded to Github
  • Full description of the issue provided (see below)

Steps to Reproduce

  1. Enable security
  2. Enable metrics
  3. Invoke endpoint that requires auth, but without credentials

Expected Behaviour

http.server metrics with status=401 should be reported

Actual Behaviour

No metrics reported for the request.
I suspect the auth handling is done before server-filters are invoked.

Environment Information

  • Operating System: All
  • Micronaut Version: 1.0.4
  • JDK Version: 11

Example Application

  • TODO:

JWKS verification failing

I have two apps running locally, an auth server;

micronaut:
  application:
    name: auth
  security:
    enabled: true
    endpoints:
      login:
        enabled: true
        path: auth/login
      oauth:
        enabled: true
        path: auth/oauth
      keys:
        enabled: true
        path: auth/keys
    token:
      jwt:
        enabled: true

Which implements a custom JwkProvider and RSASignatureGeneratorConfiguration;

@Singleton
public class RotatingJwksProvider implements JwkProvider {

    @Inject
    KeyPairRepository keyPairRepository;


    @Override
    public List<JWK> retrieveJsonWebKeys() {
        List<JWK> retval = new ArrayList<>();
        List<KeyPair> keyPairs = keyPairRepository.getAll();
        for (KeyPair keyPair : keyPairs) {
            RSAPublicKey rsaPublicKey = keyPair.getRSAPublicKey();

            RSAKey jwk = new RSAKey.Builder(rsaPublicKey)
                    .keyID(keyPair.getId())
                    .keyUse(KeyUse.SIGNATURE)
                    .algorithm(JWSAlgorithm.RS256)
                    .build();
            retval.add(jwk.toPublicJWK());

        }
        return retval;
    }

}
@Singleton
@Named("generator")
public class RotatingRSASignatureConfiguration implements RSASignatureGeneratorConfiguration {

    private final KeyPairRepository keyPairRepository;
    private static KeyPair keyPair = null;

    @Inject
    public RotatingRSASignatureConfiguration(KeyPairRepository keyPairRepository) {
        this.keyPairRepository = keyPairRepository;
    }

    private synchronized void ensureKeyPair() {
        if (keyPair == null) {
            keyPair = keyPairRepository.getLatest();
        }
        if (keyPair == null) {
            keyPair = keyPairRepository.save(KeyPair.factory());
        }
    }

    @Override
    public RSAPublicKey getPublicKey() {
        ensureKeyPair();
        return keyPair.getRSAPublicKey();
    }

    @Override
    public RSAPrivateKey getPrivateKey() {
        ensureKeyPair();
        return keyPair.getRSAPrivateKey();
    }

    @Override
    public JWSAlgorithm getJwsAlgorithm() {
        return JWSAlgorithm.RS256;
    }
}

And a client server;

micronaut:
  server:
    port: 8081
  security:
    enabled: true
    endpoints:
      login:
        enabled: false
      oauth:
        enabled: false
    token:
      jwt:
        enabled: true
        signatures:
          jwks:
            auth:
              url: "http://localhost:8080/auth/keys"

I can login to the auth server and receive a JWT token;

curl -X POST \
  http://localhost:8080/auth/login \
  -H 'Content-Type: application/json' \
  -d '{
    "username": "jamie",
    "password": "xxx"
}'

{"username":"jamie","roles":["admintestrole"],"access_token":"xxx","refresh_token":"xxx","token_type":"Bearer","expires_in":3600}

And I have successfully verified the generated JWT (and public key) using jwt.io;

Header:

{
  "alg": "RS256"
}

Payload:

{
  "sub": "jamie",
  "nbf": 1559404743,
  "roles": [
    "admintestrole"
  ],
  "iss": "auth",
  "exp": 1559408343,
  "iat": 1559404743
}

However when I attempt to use that access token against the client service, it fails;

curl -v http://localhost:8081/jctest   -H 'Authorization: Bearer xxx'   -H 'Content-Type: application/json'
*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8081 (#0)
> GET /jctest HTTP/1.1
> Host: localhost:8081
> User-Agent: curl/7.54.0
> Accept: */*
> Authorization: Bearer xxx
> Content-Type: application/json
> 
< HTTP/1.1 401 Unauthorized
< Date: Sat, 1 Jun 2019 17:02:25 GMT
< transfer-encoding: chunked
< connection: close
< 
* Closing connection 0

And the following is displayed in the logs;

2019-06-01 17:52:05  DEBUG JwtTokenValidator:130 - JWT is signed
2019-06-01 17:52:05  DEBUG JwtTokenValidator:154 - No algorithms are supported
2019-06-01 17:52:05  DEBUG JwtTokenValidator:159 - No signature algorithm found for JWT: xxx

I have also confirmed the client service is accessing the keys endpoint of the auth service;

2019-06-01 17:14:48  DEBUG SecurityFilter:157 - Authorized request GET /auth/keys. The rule provider io.micronaut.security.rules.SecuredAnnotationRule authorized the request.
2019-06-01 17:14:48  DEBUG RoutingInBoundHandler:1307 - Encoding emitted response object [{"keys":[{"kty":"RSA","e":"AQAB","use":"sig","kid":"b4224a75-a902-46ea-a4fc-23d5b14153e2","alg":"RS256","n":"1uZ7e3YPcbHFDwMSlm8CN6WH4hpA6VxIoNdfiyPL49Vww8DrInzpBDFnGlQ9kwg-5Dzif_fGXF3BI4Uxzs-lyB3GLNmNPx-MDfZL63d7PE-Ydcj4Y6xEy6siH2IyXK3WFY-s_fX8ewT1Gam0rvvcFkvuOA0voCOrnX92-LvcdGDtJbsdFskI60JLfRNiVab21T8BsrY4xw8qZeZwDgUBQyVEz38wLOi2aXy9wTI7jIDhBweEOcl6Jz2nFymEModRbdLc5gBrQ6E6KPLfiR0KaXjWuAUjhh06iN9_ureKloFQiYp3CKUcDnEhnuIr53cz42SId06cPTEvZPcplo7BIQ"}]}] using codec: io.micronaut.runtime.http.codec.TextPlainCodec@1e30a519

Is there something I have missed?

Thanks!

DefaultAuthorizationResponseHandler should not use RxHttpClient.create

Currently does this:

https://github.com/micronaut-projects/micronaut-oauth2/blob/02d2a567675e0fed363c90e3fca3ae2c77d88d11/oauth2/src/main/java/io/micronaut/security/oauth2/handlers/DefaultAuthorizationResponseHandler.java#L66

  1. If dynamic creation is required then ctx.createBean(RxHttpClient.class, url) should be used otherwise distributed tracing, metrics etc. is bypassed. Ideally however a shared client could be used for this case.
  2. If a client is created then it needs be closed. There is no call to close() to cleanup the client in this code.

Invalid Cannot decrypt / verify JWT error message

When using JWT along with basic authentication method and the latter is used, the error following error message is reported in the log file:

Cannot decrypt / verify JWT: Invalid JWT serialization: Missing dot delimiter

My understanding is that MN tries to decode the Basic auth token as it were a JWT token, tehn fails and fallback on the basic auth provider.

Refactor Token Validation

The token validator should probably be a prototype bean, or perhaps not a bean at all and instead use a builder style syntax to allow for multiple types of validation in a single app. Current use case is JWT validation of OpenID tokens as well as validation of normal authorization tokens

Setting micronaut.session.http.cookie-max-age throws an Exception

Micronaut: 1.2.0

The bellow settings throws an Exception.
micronaut.session.http.cookie-max-age: 60
micronaut.session.http.cookie.cookie-max-age: 60s

20:33:27.966 [nioEventLoopGroup-1-18] ERROR i.m.h.s.netty.RoutingInBoundHandler - Exception occurred executing error handler. Falling back to default error handling: Failed to inject value for parameter [cookieMaxAge] of method [setCookieMaxAge] of class: io.micronaut.session.http.HttpSessionConfiguration

Message: Error resolving property value [micronaut.session.http.cookie-max-age]. Property doesn't exist
Path Taken: new HttpSessionFilter(SessionStore sessionStore,[HttpSessionIdResolver[] resolvers],HttpSessionIdEncoder[] encoders) --> new CookieHttpSessionStrategy([HttpSessionConfiguration configuration]) --> HttpSessionConfiguration.setCookieMaxAge([TemporalAmount cookieMaxAge])
io.micronaut.context.exceptions.DependencyInjectionException: Failed to inject value for parameter [cookieMaxAge] of method [setCookieMaxAge] of class: io.micronaut.session.http.HttpSessionConfiguration

Message: Error resolving property value [micronaut.session.http.cookie-max-age]. Property doesn't exist
Path Taken: new HttpSessionFilter(SessionStore sessionStore,[HttpSessionIdResolver[] resolvers],HttpSessionIdEncoder[] encoders) --> new CookieHttpSessionStrategy([HttpSessionConfiguration configuration]) --> HttpSessionConfiguration.setCookieMaxAge([TemporalAmount cookieMaxAge])
        at io.micronaut.context.AbstractBeanDefinition.getValueForMethodArgument(AbstractBeanDefinition.java:773)
        at io.micronaut.session.http.$HttpSessionConfigurationDefinition.injectBean(Unknown Source)
        at io.micronaut.session.http.$HttpSessionConfigurationDefinition.build(Unknown Source)
        at io.micronaut.context.DefaultBeanContext.doCreateBean(DefaultBeanContext.java:1535)
        at io.micronaut.context.DefaultBeanContext.createAndRegisterSingleton(DefaultBeanContext.java:2248)
        at io.micronaut.context.DefaultBeanContext.getBeanForDefinition(DefaultBeanContext.java:1917)
        at io.micronaut.context.DefaultBeanContext.getBeanInternal(DefaultBeanContext.java:1892)
        at io.micronaut.context.DefaultBeanContext.getBean(DefaultBeanContext.java:1021)
        at io.micronaut.context.AbstractBeanDefinition.getBeanForConstructorArgument(AbstractBeanDefinition.java:985)
        at io.micronaut.session.http.$CookieHttpSessionStrategyDefinition.build(Unknown Source)
        at io.micronaut.context.DefaultBeanContext.doCreateBean(DefaultBeanContext.java:1535)
        at io.micronaut.context.DefaultBeanContext.addCandidateToList(DefaultBeanContext.java:2581)
        at io.micronaut.context.DefaultBeanContext.getBeansOfTypeInternal(DefaultBeanContext.java:2503)
        at io.micronaut.context.DefaultBeanContext.getBeansOfType(DefaultBeanContext.java:870)
        at io.micronaut.context.AbstractBeanDefinition.lambda$getBeansOfTypeForConstructorArgument$10(AbstractBeanDefinition.java:1092)
        at io.micronaut.context.AbstractBeanDefinition.resolveBeanWithGenericsFromConstructorArgument(AbstractBeanDefinition.java:1730)
        at io.micronaut.context.AbstractBeanDefinition.getBeansOfTypeForConstructorArgument(AbstractBeanDefinition.java:1087)
        at io.micronaut.context.AbstractBeanDefinition.getBeanForConstructorArgument(AbstractBeanDefinition.java:959)
        at io.micronaut.session.http.$HttpSessionFilterDefinition.build(Unknown Source)
        at io.micronaut.context.DefaultBeanContext.doCreateBean(DefaultBeanContext.java:1535)
        at io.micronaut.context.DefaultBeanContext.createAndRegisterSingleton(DefaultBeanContext.java:2248)
        at io.micronaut.context.DefaultBeanContext.getBeanForDefinition(DefaultBeanContext.java:1917)
        at io.micronaut.context.DefaultBeanContext.getBeanInternal(DefaultBeanContext.java:1892)
        at io.micronaut.context.DefaultBeanContext.getBean(DefaultBeanContext.java:582)
        at io.micronaut.web.router.AnnotatedFilterRouteBuilder.lambda$process$0(AnnotatedFilterRouteBuilder.java:76)
        at io.micronaut.web.router.DefaultFilterRoute.getFilter(DefaultFilterRoute.java:66)
        at io.micronaut.web.router.DefaultFilterRoute.match(DefaultFilterRoute.java:82)
        at io.micronaut.web.router.DefaultRouter.findFilters(DefaultRouter.java:236)
        at io.micronaut.http.server.netty.RoutingInBoundHandler.filterPublisher(RoutingInBoundHandler.java:1476)
        at io.micronaut.http.server.netty.RoutingInBoundHandler.exceptionCaughtInternal(RoutingInBoundHandler.java:331)
        at io.micronaut.http.server.netty.RoutingInBoundHandler.exceptionCaught(RoutingInBoundHandler.java:247)
        at io.netty.channel.AbstractChannelHandlerContext.invokeExceptionCaught(AbstractChannelHandlerContext.java:297)
        at io.netty.channel.AbstractChannelHandlerContext.notifyHandlerException(AbstractChannelHandlerContext.java:831)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:376)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352)
        at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:108)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352)
        at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:102)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352)
        at io.micronaut.http.netty.stream.HttpStreamsHandler.channelRead(HttpStreamsHandler.java:191)
        at io.micronaut.http.netty.stream.HttpStreamsServerHandler.channelRead(HttpStreamsServerHandler.java:121)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352)
        at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:102)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352)
        at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:102)
        at io.netty.handler.codec.MessageToMessageCodec.channelRead(MessageToMessageCodec.java:111)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352)
        at io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:93)
        at io.netty.handler.codec.http.HttpServerKeepAliveHandler.channelRead(HttpServerKeepAliveHandler.java:64)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352)
        at io.netty.handler.flow.FlowControlHandler.dequeue(FlowControlHandler.java:186)
        at io.netty.handler.flow.FlowControlHandler.channelRead(FlowControlHandler.java:152)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352)
        at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:438)
        at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:328)
        at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:302)
        at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:253)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352)
        at io.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:287)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352)
        at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1421)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
        at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:930)
        at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
        at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:697)
        at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:632)
        at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:549)
        at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:511)
        at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:918)
        at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
        at java.base/java.lang.Thread.run(Thread.java:835)
20:33:27.966 [nioEventLoopGroup-1-18] ERROR i.m.h.s.netty.RoutingInBoundHandler - Unexpected error occurred: Failed to inject value for parameter [cookieMaxAge] of method [setCookieMaxAge] of class: io.micronaut.session.http.HttpSessionConfiguration

Message: Error resolving property value [micronaut.session.http.cookie-max-age]. Property doesn't exist
Path Taken: new HttpSessionFilter(SessionStore sessionStore,[HttpSessionIdResolver[] resolvers],HttpSessionIdEncoder[] encoders) --> new CookieHttpSessionStrategy([HttpSessionConfiguration configuration]) --> HttpSessionConfiguration.setCookieMaxAge([TemporalAmount cookieMaxAge])
io.micronaut.context.exceptions.DependencyInjectionException: Failed to inject value for parameter [cookieMaxAge] of method [setCookieMaxAge] of class: io.micronaut.session.http.HttpSessionConfiguration

Message: Error resolving property value [micronaut.session.http.cookie-max-age]. Property doesn't exist
Path Taken: new HttpSessionFilter(SessionStore sessionStore,[HttpSessionIdResolver[] resolvers],HttpSessionIdEncoder[] encoders) --> new CookieHttpSessionStrategy([HttpSessionConfiguration configuration]) --> HttpSessionConfiguration.setCookieMaxAge([TemporalAmount cookieMaxAge])
        at io.micronaut.context.AbstractBeanDefinition.getValueForMethodArgument(AbstractBeanDefinition.java:773)
        at io.micronaut.session.http.$HttpSessionConfigurationDefinition.injectBean(Unknown Source)
        at io.micronaut.session.http.$HttpSessionConfigurationDefinition.build(Unknown Source)
        at io.micronaut.context.DefaultBeanContext.doCreateBean(DefaultBeanContext.java:1535)
        at io.micronaut.context.DefaultBeanContext.createAndRegisterSingleton(DefaultBeanContext.java:2248)
        at io.micronaut.context.DefaultBeanContext.getBeanForDefinition(DefaultBeanContext.java:1917)
        at io.micronaut.context.DefaultBeanContext.getBeanInternal(DefaultBeanContext.java:1892)
        at io.micronaut.context.DefaultBeanContext.getBean(DefaultBeanContext.java:1021)
        at io.micronaut.context.AbstractBeanDefinition.getBeanForConstructorArgument(AbstractBeanDefinition.java:985)
        at io.micronaut.session.http.$CookieHttpSessionStrategyDefinition.build(Unknown Source)
        at io.micronaut.context.DefaultBeanContext.doCreateBean(DefaultBeanContext.java:1535)
        at io.micronaut.context.DefaultBeanContext.addCandidateToList(DefaultBeanContext.java:2581)
        at io.micronaut.context.DefaultBeanContext.getBeansOfTypeInternal(DefaultBeanContext.java:2503)
        at io.micronaut.context.DefaultBeanContext.getBeansOfType(DefaultBeanContext.java:870)
        at io.micronaut.context.AbstractBeanDefinition.lambda$getBeansOfTypeForConstructorArgument$10(AbstractBeanDefinition.java:1092)
        at io.micronaut.context.AbstractBeanDefinition.resolveBeanWithGenericsFromConstructorArgument(AbstractBeanDefinition.java:1730)
        at io.micronaut.context.AbstractBeanDefinition.getBeansOfTypeForConstructorArgument(AbstractBeanDefinition.java:1087)
        at io.micronaut.context.AbstractBeanDefinition.getBeanForConstructorArgument(AbstractBeanDefinition.java:959)
        at io.micronaut.session.http.$HttpSessionFilterDefinition.build(Unknown Source)
        at io.micronaut.context.DefaultBeanContext.doCreateBean(DefaultBeanContext.java:1535)
        at io.micronaut.context.DefaultBeanContext.createAndRegisterSingleton(DefaultBeanContext.java:2248)
        at io.micronaut.context.DefaultBeanContext.getBeanForDefinition(DefaultBeanContext.java:1917)
        at io.micronaut.context.DefaultBeanContext.getBeanInternal(DefaultBeanContext.java:1892)
        at io.micronaut.context.DefaultBeanContext.getBean(DefaultBeanContext.java:582)
        at io.micronaut.web.router.AnnotatedFilterRouteBuilder.lambda$process$0(AnnotatedFilterRouteBuilder.java:76)
        at io.micronaut.web.router.DefaultFilterRoute.getFilter(DefaultFilterRoute.java:66)
        at io.micronaut.web.router.DefaultFilterRoute.match(DefaultFilterRoute.java:82)
        at io.micronaut.web.router.DefaultRouter.findFilters(DefaultRouter.java:236)
        at io.micronaut.http.server.netty.RoutingInBoundHandler.filterPublisher(RoutingInBoundHandler.java:1476)
        at io.micronaut.http.server.netty.RoutingInBoundHandler.exceptionCaughtInternal(RoutingInBoundHandler.java:331)
        at io.micronaut.http.server.netty.RoutingInBoundHandler.exceptionCaught(RoutingInBoundHandler.java:247)
        at io.netty.channel.AbstractChannelHandlerContext.invokeExceptionCaught(AbstractChannelHandlerContext.java:297)
        at io.netty.channel.AbstractChannelHandlerContext.notifyHandlerException(AbstractChannelHandlerContext.java:831)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:376)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352)
        at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:108)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352)
        at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:102)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352)
        at io.micronaut.http.netty.stream.HttpStreamsHandler.channelRead(HttpStreamsHandler.java:191)
        at io.micronaut.http.netty.stream.HttpStreamsServerHandler.channelRead(HttpStreamsServerHandler.java:121)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352)
        at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:102)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352)
        at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:102)
        at io.netty.handler.codec.MessageToMessageCodec.channelRead(MessageToMessageCodec.java:111)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352)
        at io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:93)
        at io.netty.handler.codec.http.HttpServerKeepAliveHandler.channelRead(HttpServerKeepAliveHandler.java:64)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352)
        at io.netty.handler.flow.FlowControlHandler.dequeue(FlowControlHandler.java:186)
        at io.netty.handler.flow.FlowControlHandler.channelRead(FlowControlHandler.java:152)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352)
        at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:438)
        at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:328)
        at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:302)
        at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:253)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352)
        at io.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:287)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352)
        at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1421)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
        at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:930)
        at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
        at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:697)
        at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:632)
        at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:549)
        at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:511)
        at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:918)
        at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
        at java.base/java.lang.Thread.run(Thread.java:835)

Environment:
OS: MacOS 10.14.6
JDK: jdk12
Micronaut: 1.2.0

Refactor RejectionHandler

Remove the RejectionHandler interface entirely and throw an exception instead. This can then be handled with an ExceptionHandler or a global/local error handler.

The setting micronaut.security.token.jwt.cookie.cookie-max-age throws an Exception

Micronaut: 1.1.0

The bellow settings throws an Exception.
micronaut.security.token.jwt.cookie.cookie-max-age: 86400
micronaut.security.token.jwt.cookie.cookie-max-age: 86400s

Debugging, DefaultConversionService didn't find a TypeConverter that for String -> TemporalAmount.

09:01:35.756 [nioEventLoopGroup-1-2] ERROR i.m.h.s.netty.RoutingInBoundHandler - Unexpected error occurred: Failed to inject value for parameter [cookieMaxAge] of method [setCookieMaxAge] of class: io.micronaut.security.token.jwt.cookie.JwtCookieConfigurationProperties

Message: Error resolving property value [micronaut.security.token.jwt.cookie.cookie-max-age]. Property doesn't exist
Path Taken: new SecurityFilter(Collection securityRules,[Collection authenticationFetchers],RejectionHandler rejectionHandler,SecurityFilterOrderProvider securityFilterOrderProvider) --> new TokenAuthenticationFetcher(Collection tokenValidators,[TokenResolver tokenResolver],ApplicationEventPublisher eventPublisher) --> new DefaultTokenResolver([Collection tokenReaders]) --> new JwtCookieTokenReader([JwtCookieConfiguration jwtCookieConfiguration]) --> JwtCookieConfigurationProperties.setCookieMaxAge([TemporalAmount cookieMaxAge])
io.micronaut.context.exceptions.DependencyInjectionException: Failed to inject value for parameter [cookieMaxAge] of method [setCookieMaxAge] of class: io.micronaut.security.token.jwt.cookie.JwtCookieConfigurationProperties

Document AuthenticationFetcher

I think some documentation is required for users who want to integrate with micronaut security but using custom authentication strategies.

I pass an Authorization header up to my service and then use it to produce some roles through my own methodology (not important here). My main goal is to use the @secured annotation to secure my endpoints and handle setting up the security context myself.

After reading through the source code, I discovered a pretty simple method to do this but it took quite a bit of digging.

I basically do the following.

@Singleton
public class CustomAuthenticationFetcher implements AuthenticationFetcher {
    private final CustomAuthenticationProvider customAuthenticationProvider;

    @Inject
    public CustomAuthenticationFetcher (final CustomAuthenticationProvider customAuthenticationProvider) {
        this.customAuthenticationProvider = customAuthenticationProvider;
    }

    @Override
    public Publisher<Authentication> fetchAuthentication(final HttpRequest<?> request) {
        return request.getHeaders().getAuthorization()
            .map(customAuthenticationProvider::getAuthentication)
            .orElseGet(Flowable::empty);
    }
}

Then I simply implement Authentication in CustomAuthentication.

I essentially have 2 questions.

  1. Will the above method cause any problems either now or in the likely future?
  2. Am I missing some important documentation somewhere?

Add support for OpenID Connect (OIDC)

It would be nice if Micronaut supported OIDC compliant flows out of the box. That would make it easy to use external identity providers like Auth0 and Okta for authentication in Micronaut services.

OIDC Client support is priority 1, but it's also important to be able to create compliant authorization servers with Micronaut that expose public RSA keys (JWKS) for resource servers to use for signature checks.

OpenID Connect 1.0 is a simple identity layer on top of the OAuth 2.0 protocol. It allows Clients to verify the identity of the End-User based on the authentication performed by an Authorization Server, as well as to obtain basic profile information about the End-User in an interoperable and REST-like manner.

References:
https://auth0.com/blog/navigating-rs256-and-jwks/
https://connect2id.com/products/nimbus-jose-jwt/examples/validating-jwt-access-tokens
https://connect2id.com/products/nimbus-jose-jwt/examples/jwk-selectors

Default Keycloak algorithm (HS256) reported as not supported.

It's hard to say whether or not this is a valid issue, so I'll describe what I did and saw, and let you make the call. :)

In a nutshell, I took the Okta OAuth2 example and tried to configure it to use Keycloak. On the demo side, I made this change to application.yml:

        enabled: true # <1>
        oauth2:
            client-secret: '${OAUTH_CLIENT_SECRET:""}' # <2>
            client-id: 'examples' # <3>
            #issuer: '${OIDC_ISSUER_DOMAIN}/oauth2/${OIDC_ISSUER_AUTHSERVERID}' # <4>
            issuer: "http://localhost:8080/auth/realms/micronaut-demo"

I set the following environment variables:

export KEYCLOACK_DOMAIN=http://localhost:8080
export KEYCLOAK_REALM=micronaut-demo

On the Keycloak side, I created a realm, micronaut-demo, leaving everything as the default, iirc (I was following their getting started guide).

I started the micronaut application (in debug mode) and would get NPEs after authentication, which seemed really odd (especially since the debug session didn't seem to show any nulls) on this line of code (DefaultIdTokenAccessTokenResponseHandler line 62):

throw new InvalidIdTokenAccessTokenResponseException(idTokenAccessTokenResponse);

After stepping in to the call to idTokenAccessTokenResponseValidator.validate, I found that Micronaut was claiming that HS256 was not a supported algorithm (JwtTokenValidator.validateSignedJWTSignature). Putting a breakpoint at JwksSignatureFactory.jwsk, it looks like HS256 was in the list of supported algorithms, but by the time SignatureConfiguration was injected into JwtTokenValidator, the only thing that seemed to be listed was RS256.

To make authentication work correctly, I had to modify realm in Keycloak to use RS256 by default (http://localhost:8080/auth/admin/master/console/#/realms/micronaut-demo/token-settings for reference). Once I made that change, I was able to authenticate against Keycloak and successfully redirect back to the app, but something seems strange about that.

If there are any more details you need (modified local sources, keycloak realm export, etc.), don't be shy. :)

Property-based configuration of oauth2 token validation possible?

While configuring the validation of OAuth 2 tokens for a REST API developed with micronaut, we were wondering, if it should be possible just with properties configured in the application.yaml

We were only able to provide a custom RSASignaturConfiguration class to get the key configured. The class looks likes this

class CustomRSASignatureConfiguration : RSASignatureConfiguration {

    val key = """-----BEGIN PUBLIC KEY-----
    ...   
    -----END PUBLIC KEY-----""".trimIndent()

    override fun getPublicKey(): RSAPublicKey {
        return RSAPublicKeyImpl(Base64.decode(key.replace("-----BEGIN PUBLIC KEY-----", "")
                .replace("-----END PUBLIC KEY-----", "")
                .replace("\n", "")))
    }
}

Next, we were assuming, that the RSASignatureFactory would now automatically provide the right bean based on this configuration but we also had to create a custom factory class to instantiate the SignatureConfiguration:

@Factory
public class RSASignatureFactory {

    @EachBean(RSASignatureConfiguration.class)
    public SignatureConfiguration signatureConfiguration(RSASignatureConfiguration configuration) {
        return new RSASignature(configuration);
    }
}

What is the supposed way to do this? Shall it also be possible just using application properties?

Redirect user back to the page where they started, after authentication

When users enter a OAuth2-secured application through a link to a particular page or resource, they expect to continue using the application from that page, after having gone through authentication, rather than to be redirected to the application root URL.

This is how Spring OAuth2 behaves, as well as most authentication schemes I'm aware of.

Please implement this in micronaut-oauth2, and tell us roughly how we should devise a future-proof workaround, while waiting for the feature.

Can't start the app with oauth2 - NoSuchMethodError

Hi guys,

Trying to inject into my app oauth2 authentication based on your guide for OKTA, but ran into an issue trying to start the app

19:58:26.765 [main] ERROR io.micronaut.runtime.Micronaut - Error starting Micronaut server: io.micronaut.inject.annotation.DefaultAnnotationMetadata.registerAnnotationType(Lio/micronaut/core/annotation/AnnotationClassValue;)V
java.lang.NoSuchMethodError: io.micronaut.inject.annotation.DefaultAnnotationMetadata.registerAnnotationType(Lio/micronaut/core/annotation/AnnotationClassValue;)V
	at io.micronaut.security.oauth2.openid.configuration.$OpenIdConfigurationClient$InterceptedDefinitionClass$$AnnotationMetadata.<clinit>(Unknown Source)
	at io.micronaut.security.oauth2.openid.configuration.$OpenIdConfigurationClient$InterceptedDefinitionClass.<clinit>(Unknown Source)
	at java.base/java.lang.Class.forName0(Native Method)

Can you give an advice what I can check to fix the issue?

Here is my build.gradle

plugins {
    id "io.spring.dependency-management" version "1.0.6.RELEASE"
    id "com.github.johnrengelman.shadow" version "4.0.2"
    id "net.ltgt.apt-eclipse" version "0.18"
    id "net.ltgt.apt-idea" version "0.18"
}

ext {
    micronautVersion = '1.0.0.BUILD-SNAPSHOT'
}

apply plugin:"application"
apply plugin:"java"

version "0.1"
group "cloud.cert"

repositories {
    mavenLocal()
    mavenCentral()
    jcenter()
    maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
}

dependencyManagement {
    imports {
        mavenBom "io.micronaut:micronaut-bom:${micronautVersion}"
    }
}

dependencies {
    annotationProcessor "io.micronaut:micronaut-inject-java"
    annotationProcessor "io.micronaut:micronaut-validation"
    annotationProcessor "io.micronaut:micronaut-security"
    compile "io.micronaut:micronaut-security-jwt"
    compile "io.micronaut:micronaut-inject"
    compile "io.micronaut:micronaut-validation"
    compile "io.micronaut:micronaut-runtime"
    compile "io.micronaut:micronaut-http-client"
    compile "io.micronaut.configuration:micronaut-hibernate-jpa"
    compile "io.micronaut.configuration:micronaut-hibernate-validator"
    compile "io.micronaut.configuration:micronaut-jdbc-hikari"
    compile "io.micronaut.configuration:micronaut-oauth2:$micronautVersion"
    compile "javax.annotation:javax.annotation-api"
    compile "io.micronaut:micronaut-http-server-netty"
    compile "io.micronaut:micronaut-views"
    compileOnly "io.micronaut:micronaut-inject-java"
    runtime "ch.qos.logback:logback-classic:1.2.3"
    runtime "com.h2database:h2"
    runtime "org.thymeleaf:thymeleaf:3.0.11.RELEASE"
    testCompile "junit:junit:4.12"
    testCompile "io.micronaut:micronaut-inject-java"
    testCompile "org.hamcrest:hamcrest-all:1.3"
}

shadowJar {
    mergeServiceFiles()
}

run.jvmArgs('-noverify', '-XX:TieredStopAtLevel=1')

mainClassName = "com.xxx.xxxx.Application"
tasks.withType(JavaCompile){
    options.encoding = "UTF-8"
    options.compilerArgs.add('-parameters')
}

Session based authentication logout contains original session cookie

Invoking the 'logout' endpoint when using the session based authentication results in the same set-cookie session response returned as for a 'login' request.

Steps to reproduce:

  1. Clone the Git repository: git clone https://github.com/micronaut-guides/micronaut-security-session.git
  2. Run application
  3. Login

Example:

$ curl -v -X POST "http://localhost:8080/login" -H  "accept: application/json" -H  "Content-Type: application/json" -d "{\"username\":\"sherlock\",\"password\":\"password\"}"
*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> POST /login HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.54.0
> accept: application/json
> Content-Type: application/json
> Content-Length: 45
> 
* upload completely sent off: 45 out of 45 bytes
< HTTP/1.1 303 See Other
< Location: /
< Authorization-Info: a8733442-3124-4b47-8b3a-17986d779365
< set-cookie: SESSION=YTg3MzM0NDItMzEyNC00YjQ3LThiM2EtMTc5ODZkNzc5MzY1; Path=/; HTTPOnly
< Date: Thu, 9 May 2019 14:10:38 GMT
< transfer-encoding: chunked
< connection: close
< 
* Closing connection 0
  1. Logout (using session from login)

Example:

$ curl -v -X POST "http://localhost:8080/logout" -H  "accept: application/json" -H  "Content-Type: application/json" --cookie "SESSION=YTg3MzM0NDItMzEyNC00YjQ3LThiM2EtMTc5ODZkNzc5MzY1"
*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> POST /logout HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.54.0
> Cookie: SESSION=YTg3MzM0NDItMzEyNC00YjQ3LThiM2EtMTc5ODZkNzc5MzY1
> accept: application/json
> Content-Type: application/json
> 
< HTTP/1.1 303 See Other
< Location: /
< Authorization-Info: a8733442-3124-4b47-8b3a-17986d779365
< set-cookie: SESSION=YTg3MzM0NDItMzEyNC00YjQ3LThiM2EtMTc5ODZkNzc5MzY1; Path=/; HTTPOnly
< Date: Thu, 9 May 2019 14:11:51 GMT
< transfer-encoding: chunked
< connection: close
< 
* Closing connection 0

Expected: Response to logout request should set the session value to something other than the original session id (maybe blank).
Actual: The same session id is returned for the logout request as is for the login request

Environment:
OS: MacOS 10.14.3
JDK: jdk1.8.0_162
Micronaut: 1.1.0 (also updated the guided solution to 1.1.1 and the behaviour was the same)

Oauth2 Security not Using Proxy Headers

I am currently using io.micronaut.configuration:micronaut-security-oauth2:1.2.0 behind a proxy.

Task List

  • Steps to reproduce provided
  • Stacktrace (if present) provided
  • Example that reproduces the problem uploaded to Github
  • Full description of the issue provided (see below)

Steps to Reproduce

Application is running on localhost:8082 and proxy is on localhost:5000. When making a GET request to http://localhost:8082/oauth/login{/provider}, I get status 302 and Location containing redirect_uri=http://localhost:8082/oauth/callback{/provider}

Now when I make a GET request to http://localhost:5000/oauth/login{/provider} (note the port as 5000) - This is the proxy

Expected Behaviour

I expect to get a status 302 and Location containing redirect_uri=http://localhost:5000/oauth/callback{/provider} (Note the port 5000)

Actual Behaviour

I get status 302 and Location containing redirect_uri=http://localhost:8082/oauth/callback{/provider}

Environment Information

  • Operating System: Windows 10
  • Micronaut Version: 1.2.0
  • JDK Version: 11

OpenIdClientCondition evaluates to false if issuer not present.

OpenIdClientCondition evaluates to false for such configuration:

micronaut:
  security:
    oauth2:
      enabled: true
      clients:
        apple:
          client-secret: 'XXXX'
          client-id: '${APPLE_SERVICE_ID}'
          scopes:
            - 'name'
            - 'email'
          openid:
            authorization:
              url: 'https://appleid.apple.com/auth/authorize'
            token:
              url: 'https://appleid.apple.com/auth/token'

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.