Skip to content

Add ReactiveJwtIssuerAuthenticationManagerResolver and Reactive Multi Tentant Examples #7857

Closed
@davidmelia

Description

@davidmelia

Hi,

I'm trying to add oauth resource server multi tenancy support (by issuer) to my existing webflux stack (Boot 2.2, Spring Security 5.2.1) and am really struggling.

I have a couple of requests:

  1. Could you add a reactive counterpart to Add JwtIssuerAuthenticationManagerResolver #7733

  2. All the examples in https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#oauth2resourceserver-multitenancy are none reactive and I'm really struggling to get reactive to work. I've figured out the below but have had to cut and paste code from ServerBearerTokenAuthenticationConverter which seems wrong:

public class TenantAuthenticationManagerResolver implements ReactiveAuthenticationManagerResolver<ServerHttpRequest> {
  private static final Pattern authorizationPattern = Pattern.compile("^Bearer (?<token>[a-zA-Z0-9-._~+/]+)=*$", Pattern.CASE_INSENSITIVE);

  private final Map<String, String> tenants;

  private final Map<String, JwtReactiveAuthenticationManager> authenticationManagers = new ConcurrentHashMap<>();

  public TenantAuthenticationManagerResolver() {
    this.tenants = Map.of("https://url.ii.co.uk/", "https://url.ii.co.uk/");
  }

  @Override
  public Mono<ReactiveAuthenticationManager> resolve(ServerHttpRequest request) {
    return Mono.just(this.authenticationManagers.computeIfAbsent(toTenant(request), this::fromTenant));
  }

  private String toTenant(ServerHttpRequest request) {
    try {
      String token = token(request);
      return JWTParser.parse(token).getJWTClaimsSet().getIssuer();
    } catch (Exception e) {
      throw new IllegalArgumentException(e);
    }
  }

  private JwtReactiveAuthenticationManager fromTenant(String tenant) {
    return Optional.ofNullable(this.tenants.get(tenant)).map(ReactiveJwtDecoders::fromIssuerLocation).map(JwtReactiveAuthenticationManager::new)
        .orElseThrow(() -> new IllegalArgumentException("unknown tenant"));
  }

  private String token(ServerHttpRequest request) {
    String authorizationHeaderToken = resolveFromAuthorizationHeader(request.getHeaders());
    if (authorizationHeaderToken != null) {
      return authorizationHeaderToken;
    }
    return null;
  }

  private static String resolveFromAuthorizationHeader(HttpHeaders headers) {
    String authorization = headers.getFirst(HttpHeaders.AUTHORIZATION);
    if (StringUtils.startsWithIgnoreCase(authorization, "bearer")) {
      Matcher matcher = authorizationPattern.matcher(authorization);

      if (!matcher.matches()) {
        BearerTokenError error = invalidTokenError();
        throw new OAuth2AuthenticationException(error);
      }

      return matcher.group("token");
    }
    return null;
  }

  private static BearerTokenError invalidTokenError() {
    return new BearerTokenError(BearerTokenErrorCodes.INVALID_TOKEN, HttpStatus.UNAUTHORIZED, "Bearer token is malformed", "https://tools.ietf.org/html/rfc6750#section-3.1");
  }
}

Ideally I would like to implement a reactive version of https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#parsing-the-claim-only-once but cannot figure out NimbusReactiveJwtDecoder. Could you give me any pointers?

Thanks

Metadata

Metadata

Assignees

Labels

in: oauth2An issue in OAuth2 modules (oauth2-core, oauth2-client, oauth2-resource-server, oauth2-jose)type: enhancementA general enhancement

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions