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 14303d8 commit 67713a2
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 25 deletions.
40 changes: 33 additions & 7 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 (format: https://yourdomain or https://yourdomain:port) OR regular expression (format: /^https://example\\.com:port$/)")
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,39 @@ func initServerPort() error {
return nil
}

func initAudiences(rawURLs []string) error {
for _, rawURL := range rawURLs {
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(str string) (string, error) {
if strings.HasPrefix(str, "/") && strings.HasSuffix(str, "/") {
if len(str) < 3 {
return "", fmt.Errorf("Invalid audiences regular expression %q", str)
}
return str[1 : len(str)-1], nil
}
return parseCommaSeparatedAudiences(str)
}

func parseCommaSeparatedAudiences(str string) (string, error) {
var strs []string
for _, rawURL := range strings.Split(str, ",") {
aud, err := jwt.ParseAudience(rawURL)
if err != nil {
return fmt.Errorf("Invalid audience %q (%v)", rawURL, err)
return "", fmt.Errorf("Invalid audience %q (%v)", rawURL, err)
}
cfg.Audiences = append(cfg.Audiences, aud)
strs = append(strs, regexp.QuoteMeta((*url.URL)(aud).String()))
}
return nil
return fmt.Sprintf("^%s$", strings.Join(strs, "|")), 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 67713a2

Please sign in to comment.