JWT ClaimPrincipal

119 views
Skip to first unread message

David Blevins

unread,
Aug 4, 2017, 10:43:59 PM8/4/17
to Eclipse MicroProfile
We have a JWTPrincipal which provides access to the guts of the JWT, however I wonder if we want to consider ClaimPrincipal to encompase each tuple. Something similar to:

public interface ClaimPrincipal<T> extends Principal {

@Override
public String getName();

public T getValue();
}

The thinking is two-fold:

- Claim is to JWT as Principal is to Subject
- A list of entries, keys and values, plays well with streams

On the second point, consider code like the following:

// JSR-375 SecurityContext
SecurityContext securityContext = null;

final List<ClaimPrincipal<String>> somethings = securityContext.getPrincipalsByType(ClaimPrincipal.class).stream()
.filter(claim -> claim.getName().contains("something"))
.filter(claim -> String.class.isInstance(claim.getValue()))
.map(claim -> (ClaimPrincipal<String>) claim)
.collect(Collectors.toList());

Here we collect all the claims that have “something” in the name and are of type string. Additionally, we coerce them from ClaimPrincipal<Object> to ClaimPrincipal<String> so we can refer to them in a type safe way.


--
David Blevins
http://twitter.com/dblevins
http://www.tomitribe.com
310-633-3852

Alasdair Nottingham

unread,
Aug 5, 2017, 7:33:32 AM8/5/17
to microp...@googlegroups.com
If you are using CDI 1.2 how would you see getting these? I'd expect you to @Inject a JWTPrincipal with the current design, but I don't think that would be simple with your proposal. Unless you are suggesting both?

Alasdair Nottingham
> --
> You received this message because you are subscribed to the Google Groups "Eclipse MicroProfile" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to microprofile...@googlegroups.com.
> To post to this group, send email to microp...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/microprofile/A4430701-A9DA-4DCE-9726-477FBA875D85%40tomitribe.com.
> For more options, visit https://groups.google.com/d/optout.

sst...@redhat.com

unread,
Aug 5, 2017, 1:50:21 PM8/5/17
to Eclipse MicroProfile
In general the JWTPrincipal cannot be injected as it is a function of the current request. It would be available via an injected javax.ws.rs.core.SecurityContext#getUserPrincipal().

sst...@redhat.com

unread,
Aug 5, 2017, 1:53:21 PM8/5/17
to Eclipse MicroProfile
This makes sense to me for a post 1.0 MP-JWT spec as the currently required javax.ws.rs.core.SecurityContext does not have enough meat to provide access to this information.

John D. Ament

unread,
Aug 6, 2017, 9:39:44 AM8/6/17
to Eclipse MicroProfile
CDI exposes a request context, so we could make it injectable.  Hammock's JWTIdentity (which wraps the JWTPrincipal) is a request scoped bean - https://github.com/hammock-project/hammock/blob/master/security-jose/src/main/java/ws/ament/hammock/jwt/bean/JWTIdentityHolder.java#L29

John

sst...@redhat.com

unread,
Aug 6, 2017, 3:27:01 PM8/6/17
to Eclipse MicroProfile
Cool, so you can inject that as an endpoint invocation parameter, but it cannot be injected into the endpoint instance itself in general due to scope differences, or do you have a trick for that?

David Blevins

unread,
Aug 6, 2017, 4:07:08 PM8/6/17
to Eclipse MicroProfile
CDI allows you to have @RequestScoped beans injected as fields of even @ApplicationScoped beans.

We could have the entire JWT itself or individual claims injected to beans that want them. Effectively, our own config-like API where users could get values from the JWT without having to parse it themselves.


public static class Foo {

@Claim("iss")
private javax.inject.Provider<String> issuer;

@Claim("exp")
private javax.inject.Provider<Long> expiration;

}


I used javax.inject.Provider as the type, but it very well can be JWTPrincipal or ClaimPrincipal. Anything that can be proxied will work. Perhaps even java.util.function.Supplier as it is now the defacto functional interface for such use cases and used by java.util.Optional and more.

Here’s an example of how we could define the qualifier. The Supplier, Provider, JWTPrincipal or ClaimPrincipal bean itself would be supplied by the MicroProfile implementation via an extension and the spec would require the implementation to install those beans as @RequestScoped.

import javax.enterprise.util.Nonbinding;
import javax.inject.Qualifier;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
public @interface Claim {

String value() default "";
> --
> You received this message because you are subscribed to the Google Groups "Eclipse MicroProfile" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to microprofile...@googlegroups.com.
> To post to this group, send email to microp...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/microprofile/f68fe120-3757-42a2-b9d0-aa98a9162fb3%40googlegroups.com.

sst...@redhat.com

unread,
Aug 6, 2017, 5:53:00 PM8/6/17
to Eclipse MicroProfile
Ok, good to know.

Mark Struberg

unread,
Aug 7, 2017, 3:53:01 PM8/7/17
to Eclipse MicroProfile
You can use javax.inject.Provider but there is a trick you need to know!

The CDI built-in Instance<T> extends javax.inject.Provider. And Instance are always-on built-in Beans. So if you just create your own Bean for

@Inject 
@JwtClaim("iss") 
private Provider<String> issuer;

then it will blow up with an AmbiguousResolutionException. Because the CDI container will find the JwtClaim Bean PLUS it's own @Any Instance<String>.

In Mp-Config I solved this problem by making the ConfigProviderbean an isAlternative(). That way this custom bean 'wins' over the built in Instance:


LieGrue,
strub

Alasdair Nottingham

unread,
Aug 7, 2017, 4:56:15 PM8/7/17
to MicroProfile
Is a reasonable interpretation of this thread that it is possible to do CDI integration? If I’ve interpreted the thread correctly I think that the JWT-MP 1.0 spec should enable this.

Alasdair

--
You received this message because you are subscribed to the Google Groups "Eclipse MicroProfile" group.
To unsubscribe from this group and stop receiving emails from it, send an email to microprofile...@googlegroups.com.
To post to this group, send email to microp...@googlegroups.com.

David Blevins

unread,
Aug 7, 2017, 5:20:16 PM8/7/17
to Eclipse MicroProfile
On Aug 7, 2017, at 1:56 PM, Alasdair Nottingham <alasdair....@gmail.com> wrote:

Is a reasonable interpretation of this thread that it is possible to do CDI integration? If I’ve interpreted the thread correctly I think that the JWT-MP 1.0 spec should enable this.

You are correct, sir! :)  The Config API effectively injects and coerces simple strings in kev-value format using @ApplicationScoped.  We’d be doing the same thing via @RequestScoped using the JWT as the source.


-David

Alasdair Nottingham

unread,
Aug 7, 2017, 5:22:43 PM8/7/17
to microp...@googlegroups.com
Thanks, then I think that integration with CDI injection should be required. It doesn’t seem like it would be a massive change to enable, but I think it would make this much more useful.

Alasdair

Hendrik Ebbers

unread,
Aug 7, 2017, 6:19:19 PM8/7/17
to Eclipse MicroProfile
Adding my comments from twitter:

I think request scope is 100% right. Why not providing a DTO (injection without Provider) and a single claim by Provider. I think provider is more complex for beginners. Simply injection a DTO is more user friendly. Supplier support looks nice at first moment. Here frameworks like Spring could provide the same API in future but in general I think Provider interface is made for exactly this so I would start with this one.


Why does @Claim has ElementType.METHOD? 

ElementType.ANNOTATION_TYPE should be added to @Claim. By doing so specific qualifiers for default claims can be defined (like @IssuedAtClaim).


Keycloak contains a DTO for the token: org.keycloak.representations.JsonWebToken 

Maybe this can be defined as a basic interface. By doing so MicroProfile and KeyCloak (and later Spring) can share the same interface.

David Blevins

unread,
Aug 7, 2017, 7:13:45 PM8/7/17
to Eclipse MicroProfile, Hendrik Ebbers
Hello, Hendrik!  Welcome to the forum! :)


On Aug 7, 2017, at 3:19 PM, Hendrik Ebbers <hendrik...@gmail.com> wrote:

I think request scope is 100% right. Why not providing a DTO (injection without Provider) and a single claim by Provider. I think provider is more complex for beginners. Simply injection a DTO is more user friendly. Supplier support looks nice at first moment. Here frameworks like Spring could provide the same API in future but in general I think Provider interface is made for exactly this so I would start with this one.

There's a course grained object here:


It’s currently “tied” to java.security.Principal.  We could have that exact interface be injectable.  We could potentially have it take one step away from Principal and rename it JsonWebToken, then alter JWTPrincipal so it is simply:

    public interface JWTPrincipal extends Principal, JsonWebToken

Not sure there’s merit in the second part, but throwing it out there.


Why does @Claim has ElementType.METHOD? 

As noted on twitter, that’s for the @Produces side of things.  The implementation will probably need to add a producer via an Extension to supply the claims.

ElementType.ANNOTATION_TYPE should be added to @Claim. By doing so specific qualifiers for default claims can be defined (like @IssuedAtClaim).


Excellent catch!  We definitely want to support @Stereotype

Keycloak contains a DTO for the token: org.keycloak.representations.JsonWebToken 

Maybe this can be defined as a basic interface. By doing so MicroProfile and KeyCloak (and later Spring) can share the same interface.

I like that interface name quite a lot.  Simple and clean.  I suggested above we could maybe just use it.  I guess the question is do we want something that is or is not tied to javax.security.Principal.


-David

Hendrik Ebbers

unread,
Aug 7, 2017, 7:17:19 PM8/7/17
to Eclipse MicroProfile, hendrik...@gmail.com

Keycloak contains a DTO for the token: org.keycloak.representations.JsonWebToken 

Maybe this can be defined as a basic interface. By doing so MicroProfile and KeyCloak (and later Spring) can share the same interface.

I like that interface name quite a lot.  Simple and clean.  I suggested above we could maybe just use it.  I guess the question is do we want something that is or is not tied to javax.security.Principal.


The Principal interface is part of JavaSE and therefore can be simply used anywhere. Based on this it would be quite easy to integrate it here. 

Chunlong Liang

unread,
Aug 8, 2017, 11:19:46 AM8/8/17
to Eclipse MicroProfile
I prefer to stick with JWTPrincipal, as java.security.Principal is in JavaSE and simple, and is also used everywhere in Java EE API from servlet to ejb.

Alasdair Nottingham

unread,
Aug 8, 2017, 5:49:19 PM8/8/17
to microp...@googlegroups.com
Chunlong, 

I’m not sure I understand your reasoning. David has suggested reusing Principal, he is just suggesting (I think) that the Subject should contain one Principal per JWT claim. Can you elaborate any more?

It isn’t clear to me if David’s proposal is an alternative, to JWT principal or an addition. Personally I like the JWTPrincipal approach because it makes it very easy for me if my code wants to deal with a single claim. I like being able to write this:

String audience = principal.getAudience();

over:

List<String> audienceStrings = securityContext.getPrincipalsByType(ClaimPrincipal.class).stream()
          .filter(claim -> claim.getName().equals(“aud”))
          .filter(claim -> String.class.isInstance(claim.getValue())
          .map(claim -> ((ClaimPrincipal<String>)claim).getValue())
          .collect(Collectors.toList());
String audience = (audienceStrings.isEmpty()) ? null : audienceStrings.get(0);

Although I’m sure there is a simpler way than that example.

David,

Do you have any use cases where we need to be able to deal with all the claims as a group rather than extracting an individual one.

Alasdair

--
You received this message because you are subscribed to the Google Groups "Eclipse MicroProfile" group.
To unsubscribe from this group and stop receiving emails from it, send an email to microprofile...@googlegroups.com.
To post to this group, send email to microp...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages