lib/verifier/aud_verifier.go (81 lines of code) (raw):

// Copyright 2020 Google LLC. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package verifier import ( "fmt" "strings" "bitbucket.org/creachadair/stringset" /* copybara-comment */ "github.com/GoogleCloudPlatform/healthcare-federated-access-services/lib/ga4gh" /* copybara-comment: ga4gh */ ) type audienceVerifier interface { Verify(claims *ga4gh.StdClaims, opts ...Option) error } type passportAudienceVerifier struct { clientID string } func (s *passportAudienceVerifier) Verify(claims *ga4gh.StdClaims, opts ...Option) error { // passport requires audience must contain the given client claims. if stringset.Contains([]string(claims.Audience), s.clientID) { return nil } return fmt.Errorf("token does not have required audience") } type visaAudienceVerifier struct { prefix string } func (s *visaAudienceVerifier) Verify(claims *ga4gh.StdClaims, opts ...Option) error { // accept visa with empty audience if len(claims.Audience) == 0 { return nil } // Audience is set. Reject if given prefix is empty as we cannot ensure a match. if len(s.prefix) == 0 { return fmt.Errorf("token has audience but no prefix is set to allow") } // visa audience not empty, must have a audience has the given prefix for _, a := range claims.Audience { if strings.HasPrefix(a, s.prefix) { return nil } } return fmt.Errorf("token does not have an audience with given prefix") } type accessTokenAudienceVerifier struct { } type accessTokenOption struct { clientID string self string useAzp bool } func (s *accessTokenOption) isOption() {} // AccessTokenOption for verifier aud/azp claims. func AccessTokenOption(clientID, self string, useAzp bool) Option { return &accessTokenOption{ clientID: clientID, self: self, useAzp: useAzp, } } func (s *accessTokenAudienceVerifier) Verify(claims *ga4gh.StdClaims, opts ...Option) error { var opt *accessTokenOption for _, o := range opts { if a, ok := o.(*accessTokenOption); ok { opt = a break } } if opt == nil { return fmt.Errorf("need accessTokenOption to verify aud/azp") } if len(claims.AuthorizedParty) == 0 && len(claims.Audience) == 0 { // Is a public token. return nil } if len(opt.self) > 0 { if opt.useAzp && opt.self == claims.AuthorizedParty { return nil } if stringset.Contains([]string(claims.Audience), opt.self) { return nil } } if opt.useAzp && opt.clientID == claims.AuthorizedParty { return nil } if stringset.Contains([]string(claims.Audience), opt.clientID) { return nil } return fmt.Errorf("token does not have a required audience or a required azp") }