Skip to content

Commit

Permalink
added: support for audiences as regexp
Browse files Browse the repository at this point in the history
  • Loading branch information
imkira committed Jun 8, 2017
1 parent 9c19284 commit 818b55c
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 26 deletions.
50 changes: 42 additions & 8 deletions config.go
Expand Up @@ -3,7 +3,9 @@ package main
import (
"errors"
"fmt"
"net/url"
"os"
"regexp"
"strconv"
"strings"

Expand All @@ -17,7 +19,7 @@ var (
cfg = &jwt.Config{}
listenAddr = flag.String("listen-addr", "0.0.0.0", "Listen address")
listenPort = flag.String("listen-port", "", "Listen port (default: 80 for HTTP or 443 for HTTPS)")
audiences = flag.String("audiences", "", "Comma separated list of JWT Audiences (format: https://yourdomain or https://yourdomain:port)")
audiences = flag.String("audiences", "", "Comma-separated list of JWT Audiences (elements can be URLs like \"https://exammple.com:port\" or regular expressions like \"/^https://example\\.com:port$/\" if you enclose them in slashes)")
publicKeysPath = flag.String("public-keys", "", "Path to public keys file (optional)")
tlsCertPath = flag.String("tls-cert", "", "Path to TLS server's, intermediate's and CA's PEM certificate (optional)")
tlsKeyPath = flag.String("tls-key", "", "Path to TLS server's PEM key file (optional)")
Expand All @@ -35,7 +37,7 @@ func initConfig() error {
if len(*audiences) == 0 {
return errors.New("You must specify --audiences")
}
if err := initAudiences(strings.Split(*audiences, ",")); err != nil {
if err := initAudiences(*audiences); err != nil {
return err
}
if err := initPublicKeys(*publicKeysPath); err != nil {
Expand All @@ -58,15 +60,47 @@ func initServerPort() error {
return nil
}

func initAudiences(rawURLs []string) error {
for _, rawURL := range rawURLs {
aud, err := jwt.ParseAudience(rawURL)
func initAudiences(audiences string) error {
str, err := extractAudiencesRegexp(audiences)
if err != nil {
return err
}
re, err := regexp.Compile(str)
if err != nil {
return fmt.Errorf("Invalid audiences regular expression %q (%v)", str, err)
}
cfg.MatchAudiences = re
return nil
}

func extractAudiencesRegexp(audiences string) (string, error) {
var strs []string
for _, audience := range strings.Split(audiences, ",") {
str, err := extractAudienceRegexp(audience)
if err != nil {
return fmt.Errorf("Invalid audience %q (%v)", rawURL, err)
return "", err
}
cfg.Audiences = append(cfg.Audiences, aud)
strs = append(strs, str)
}
return nil
return strings.Join(strs, "|"), nil
}

func extractAudienceRegexp(audience string) (string, error) {
if strings.HasPrefix(audience, "/") && strings.HasSuffix(audience, "/") {
if len(audience) < 3 {
return "", fmt.Errorf("Invalid audiences regular expression %q", audience)
}
return audience[1 : len(audience)-1], nil
}
return parseRawAudience(audience)
}

func parseRawAudience(audience string) (string, error) {
aud, err := jwt.ParseAudience(audience)
if err != nil {
return "", fmt.Errorf("Invalid audience %q (%v)", audience, err)
}
return fmt.Sprintf("^%s$", regexp.QuoteMeta((*url.URL)(aud).String())), nil
}

func initPublicKeys(filePath string) error {
Expand Down
2 changes: 1 addition & 1 deletion jwt/claims.go
Expand Up @@ -26,7 +26,7 @@ func (c Claims) Valid() error {
if err != nil {
return fmt.Errorf("Invalid audience %q: %v", c.Audience, err)
}
if !c.cfg.containsAudience(aud) {
if !c.cfg.matchesAudience(aud) {
return fmt.Errorf("Unexpected audience: %q", c.Audience)
}
return nil
Expand Down
28 changes: 11 additions & 17 deletions jwt/config.go
@@ -1,35 +1,29 @@
package jwt

import "errors"
import (
"errors"
"net/url"
"regexp"
)

// Config specifies the parameters for which to perform validation of JWT
// tokens in requests against.
type Config struct {
PublicKeys map[string]PublicKey
Audiences []*Audience
PublicKeys map[string]PublicKey
MatchAudiences *regexp.Regexp
}

// Validate validates the Configuration.
func (cfg *Config) Validate() error {
if len(cfg.Audiences) == 0 {
return errors.New("No audiences defined")
}
for _, aud := range cfg.Audiences {
if err := aud.Validate(); err != nil {
return err
}
if cfg.MatchAudiences == nil {
return errors.New("No audiences to match defined")
}
if len(cfg.PublicKeys) == 0 {
return errors.New("No public keys defined")
}
return nil
}

func (cfg *Config) containsAudience(aud *Audience) bool {
for _, aud2 := range cfg.Audiences {
if *aud == *aud2 {
return true
}
}
return false
func (cfg *Config) matchesAudience(aud *Audience) bool {
return cfg.MatchAudiences.MatchString((*url.URL)(aud).String())
}
2 changes: 2 additions & 0 deletions main.go
Expand Up @@ -22,6 +22,8 @@ func main() {
log.Fatal(err)
}

log.Printf("Matching audiences: %s\n", cfg.MatchAudiences)

http.HandleFunc("/auth", authHandler)
http.HandleFunc("/healthz", healthzHandler)

Expand Down

0 comments on commit 818b55c

Please sign in to comment.