keratin / authn-go

Go client library for Keratin AuthN
https://github.com/keratin/authn-server
GNU Lesser General Public License v3.0
32 stars 10 forks source link

Extended audience verification options #14

Closed ppacher closed 5 years ago

ppacher commented 5 years ago

This issue is a follow-up to the discussion in #13 where I will revert the change and wait for the outcome of the discussion on this issue.

Issue

Currently authn-go enforces audience verification against the audience set in Config.Audience. Since idTokenVerifier#L96 always passes Config.Audience to go-jose.v2/jwt.Validate it is not possible to disable audience verification at all (either for development purposes or because it's a strange requirement). In addition, it is not possible to let authn-go verify that at least one of multiple different audiences must be present in the JWT. This would allow easy integration into SSO solutions where each application (thus with a dedicated audience) needs access to common endpoint without using an application gateway (e.g. terminating TLS right before authn-server; ...)

The used JWT package go-jose.v2/jwt already supports passing multiple audiences to jwt.Validate although it then enforces the JWT to include all of those audiences rather then one or a sub-set.

Proposed solutions

Since there's already a discussion about a v2 release/client in #12 my proposed solutions here will not be backwards compatible with v1.

For the do-not-verify problem the solutions would be simple by just not specifying an audience for jwt.Validate if it's empty (or nil, see below suggestion).

In order to be able to satisfy both use-cases (the one-of and the all-of) I'd suggest the following API (implementation details to be discussed):

one-of

cfg := authn.Config {
    Issuer: "auth.example.com",
    // ... other fields
    Audience: authn.OneOfAudience("auth.example.com", "iam.example.com", "app1.example.com"),
}

The above example would count every JWT as valid as long as at least one of the above audiences is included.

all-of

cfg := authn.Config {
    Issuer: "auth.example.com",
    // ... other fields
    Audience: authn.RequiredAudiences("auth.example.com", "iam.example.com", "app1.example.com"),
}

In contrast to one-of, this will only accept JWTs that includes ALL of the mentioned audiences (which is AFAIK not really possible using authn-server right now).

Regex

As a really nice-to-have we could also extend this API with more complex verification and validation features like using regex or sub-domain matches:

cfg := authn.Config {
    Issuer: "auth.example.com",
    // ... other fields
    Audience: authn.AudienceMustMatch("lb[0-9]+\\.app\\.example\\.com"),
}
cfg := authn.Config {
    Issuer: "auth.example.com",
    // ... other fields
    Audience: authn.IsSubDomainAudience("example.com"),
}

Although, for the last tow (Regex/Subdomain) the behavior with multiple audiences would still need to be specified.

cainlevy commented 5 years ago

Thanks for writing this up. I mean for AuthN to provide SSO from multiple frontend domains to at least one shared backend. This is definitely in scope.

I don't have a use case yet for "all-of". AuthN doesn't currently sign tokens with more than one audience. Can you suggest a setup that might motivate building that level of flexibility into the API?

I also went digging through the authn-rb client and found https://github.com/keratin/authn-rb/pull/8. That changeset allowed control over the audience on a per-request basis, which provides substantial flexibility to an application with both static and variable domains based on any runtime data. Thoughts? I could imagine making it available to v1 clients as a new method like authn.SubjectFromAudience.

ppacher commented 5 years ago

I don't have a use case yet for "all-of". AuthN doesn't currently sign tokens with more than one audience. Can you suggest a setup that might motivate building that level of flexibility into the API?

I don't have a use case for that on my own and actually just added it because it's what go-jose.v2/jwt does by default if you provide multiple audiences. Unfortunately I didn'd find a use-case in their issues.

Actually something like done in keratin/authn-rb#8 would also be awesome. Didn't find that PR.

For the flexibility I think adding an "AudienceMatcher" interface would would not add much complexity and one could implement what ever audience matching is required, also the per-request matching could be done this way.

But adding authn.SubjectFromAudience that allows specifying the audience used for verification will be enough. Though I think the naming is a bit confusing.

cainlevy commented 5 years ago

Yeah that naming could be better. 😄

We could also add audience as an optional parameter to the existing authn.SubjectFrom. Maybe that paves the way to other customizable verification strategies.