-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 281b68a
Showing
7 changed files
with
480 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package validateiap | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
|
||
"cloud.google.com/go/compute/metadata" | ||
) | ||
|
||
func getAudience() (string, error) { | ||
// Return the audience set in the environment first, | ||
// then fall back the auto-generating an App Engine compatible value. | ||
if aud := os.Getenv("IAP_AUDIENCE"); aud != "" { | ||
return aud, nil | ||
} else { | ||
return getAppEngineAudience() | ||
} | ||
} | ||
|
||
func getAppEngineAudience() (string, error) { | ||
projectNumber, err := metadata.NumericProjectID() | ||
if err != nil { | ||
return "", fmt.Errorf("metadata.NumericProjectID: %v", err) | ||
} | ||
|
||
projectID, err := metadata.ProjectID() | ||
if err != nil { | ||
return "", fmt.Errorf("metadata.ProjectID: %v", err) | ||
} | ||
|
||
return "/projects/" + projectNumber + "/apps/" + projectID, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package validateiap | ||
|
||
import ( | ||
"fmt" | ||
"net/http" | ||
"strings" | ||
) | ||
|
||
func GetUserEmail(r *http.Request) (string, error) { | ||
email := r.Header.Get("X-Goog-Authenticated-User-Email") | ||
if email == "" { | ||
return "", fmt.Errorf("Authenticated email is blank") | ||
} | ||
|
||
return strings.Replace(email, "accounts.google.com:", "", 1), nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
module github.com/a1comms/go-middleware-validate-iap | ||
|
||
go 1.13 | ||
|
||
require ( | ||
cloud.google.com/go v0.56.0 | ||
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect | ||
github.com/imkira/gcp-iap-auth v0.0.4-0.20190125075610-2aea4f92016e | ||
github.com/urfave/negroni v1.0.0 | ||
) |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package validateiap | ||
|
||
import ( | ||
"net/http" | ||
) | ||
|
||
func LogoutHandler(w http.ResponseWriter, r *http.Request) { | ||
http.Redirect(w, r, "/_gcp_iap/clear_login_cookie", 302) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package validateiap | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"regexp" | ||
"strings" | ||
|
||
"github.com/imkira/gcp-iap-auth/jwt" | ||
) | ||
|
||
var cfg *jwt.Config | ||
|
||
func init() { | ||
cfg = &jwt.Config{} | ||
|
||
aud, err := getAudience() | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
err = initAudiences(aud) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
cfg.PublicKeys, err = jwt.FetchPublicKeys() | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
if err := cfg.Validate(); err != nil { | ||
log.Fatal(err) | ||
} | ||
} | ||
|
||
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 "", err | ||
} | ||
strs = append(strs, str) | ||
} | ||
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) { | ||
_, err := jwt.ParseAudience(audience) | ||
if err != nil { | ||
return "", fmt.Errorf("Invalid audience %q (%v)", audience, err) | ||
} | ||
return fmt.Sprintf("^%s$", regexp.QuoteMeta(audience)), nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
package validateiap | ||
|
||
import ( | ||
"context" | ||
"log" | ||
"net/http" | ||
|
||
"github.com/imkira/gcp-iap-auth/jwt" | ||
"github.com/urfave/negroni" | ||
) | ||
|
||
type emailValFunc func(context.Context, string) (bool, error) | ||
|
||
var ( | ||
ValidateIAPMiddleware negroni.HandlerFunc = GetValidateIAPMiddleware(emailNotEmpty) | ||
ValidateIAPAppEngineMiddleware negroni.HandlerFunc = GetValidateIAPAppEngineMiddleware(emailNotEmpty) | ||
) | ||
|
||
func GetValidateIAPMiddleware(emailVal emailValFunc) negroni.HandlerFunc { | ||
return func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { | ||
if claims, err := jwt.RequestClaims(r, cfg); err != nil { | ||
log.Printf("ValidateIAP: Failed to validate request claims: %s", err) | ||
} else { | ||
if ok, err := emailVal(r.Context(), claims.Email); err != nil { | ||
log.Printf("ValidateIAP: Failed to call email validation function: %s", err) | ||
} else if ok { | ||
next(w, r) | ||
return | ||
} | ||
} | ||
|
||
http.Error(w, "Unauthorized", http.StatusUnauthorized) | ||
} | ||
} | ||
|
||
func GetValidateIAPAppEngineMiddleware(emailVal emailValFunc) negroni.HandlerFunc { | ||
return func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { | ||
if val := r.Header.Get("X-AppEngine-Cron"); val != "" { | ||
next(w, r) | ||
return | ||
} else if val := r.Header.Get("X-AppEngine-QueueName"); val != "" { | ||
next(w, r) | ||
return | ||
} else if claims, err := jwt.RequestClaims(r, cfg); err == nil { | ||
if ok, err := emailVal(r.Context(), claims.Email); err != nil { | ||
log.Printf("ValidateIAP: Failed to call email validation function: %s", err) | ||
} else if ok { | ||
next(w, r) | ||
return | ||
} | ||
} else { | ||
log.Printf("ValidateIAP: Failed to validate request claims: %s", err) | ||
} | ||
|
||
http.Error(w, "Unauthorized", http.StatusUnauthorized) | ||
} | ||
} | ||
|
||
func emailNotEmpty(ctx context.Context, email string) (bool, error) { | ||
if email != "" { | ||
return true, nil | ||
} | ||
|
||
return false, nil | ||
} |