This repository has been archived by the owner on May 2, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 5
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
Showing
9 changed files
with
297 additions
and
10 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 |
---|---|---|
|
@@ -12,3 +12,4 @@ | |
*.out | ||
|
||
*.pem | ||
config.yaml |
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,73 @@ | ||
package authentication | ||
|
||
import ( | ||
"context" | ||
"encoding/base64" | ||
"encoding/json" | ||
"errors" | ||
log "github.com/sirupsen/logrus" | ||
"golang.org/x/oauth2" | ||
"net/http" | ||
) | ||
|
||
type Auth0Provider struct { | ||
OAuth2Provider | ||
oauth2 oauth2.Config | ||
domain string | ||
} | ||
|
||
func NewAuth0Provider(config OAuth2Config) OAuth2Provider { | ||
return Auth0Provider{ | ||
domain: config.Domain, | ||
oauth2: oauth2.Config{ | ||
ClientID: config.ClientID, | ||
ClientSecret: config.ClientSecret, | ||
RedirectURL: config.CallbackURL, | ||
Scopes: []string{"openid", "email_verified", "email"}, | ||
Endpoint: oauth2.Endpoint{ | ||
AuthURL: config.AuthURL, | ||
TokenURL: config.TokenURL, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func (provider Auth0Provider) GetUserProfile(r *http.Request) (OIDCProfile, error) { | ||
var profile OIDCProfile | ||
code := r.URL.Query().Get("code") | ||
|
||
token, err := provider.oauth2.Exchange(context.TODO(), code) | ||
if err != nil { | ||
return profile, ErrCodeExchange | ||
} | ||
|
||
// Get user profile | ||
client := provider.oauth2.Client(context.TODO(), token) | ||
resp, err := client.Get(provider.domain + "/userinfo") | ||
if err != nil { | ||
return profile, ErrProfile | ||
} | ||
|
||
defer func() { | ||
if resp.Body != nil { | ||
if err := resp.Body.Close(); err != nil { | ||
log.Error(err) | ||
} | ||
} | ||
}() | ||
|
||
if err = json.NewDecoder(resp.Body).Decode(&profile); err != nil { | ||
return profile, errors.New("auth0: cannot decode JSON profile") | ||
} | ||
|
||
if profile.Email == "" { | ||
return profile, ErrNoEmail | ||
} | ||
|
||
return profile, nil | ||
} | ||
|
||
func (provider Auth0Provider) GetLoginURL(state string) string { | ||
s := base64.StdEncoding.EncodeToString([]byte(state)) | ||
return provider.oauth2.AuthCodeURL(s) | ||
} |
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,97 @@ | ||
package authentication | ||
|
||
import ( | ||
"encoding/base64" | ||
"errors" | ||
log "github.com/sirupsen/logrus" | ||
"net/http" | ||
"time" | ||
) | ||
|
||
type OAuth2Config struct { | ||
ClientID string | ||
ClientSecret string | ||
CallbackURL string | ||
AuthURL string | ||
TokenURL string | ||
Domain string | ||
} | ||
|
||
type OIDCProfile struct { | ||
Email string | ||
} | ||
|
||
type OAuth2Provider interface { | ||
GetUserProfile(r *http.Request) (OIDCProfile, error) | ||
GetLoginURL(state string) string | ||
} | ||
|
||
const CookieName = "Helios_Authorization" | ||
const HeaderName = "Helios-Jwt-Assertion" | ||
|
||
var ErrUnauthorized = errors.New("unauthorized request") | ||
var ErrCodeExchange = errors.New("error on code exchange") | ||
var ErrProfile = errors.New("error getting user profile") | ||
var ErrNoEmail = errors.New("no email found in user profile") | ||
|
||
func authenticate(r *http.Request) error { | ||
// look for Token in both Cookies and Headers | ||
_, err := r.Cookie(CookieName) | ||
htoken := r.Header.Get(HeaderName) | ||
|
||
if err == http.ErrNoCookie && htoken == "" { | ||
return ErrUnauthorized | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func Middleware(provider OAuth2Provider, next http.Handler) http.Handler { | ||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
if err := authenticate(r); err != nil { | ||
url := provider.GetLoginURL(r.RequestURI) | ||
log.Debugf("Redirecting to %s", url) | ||
http.Redirect(w, r, url, http.StatusTemporaryRedirect) | ||
return | ||
} | ||
|
||
log.Println(r.RequestURI) | ||
// Call the next handler, which can be another middleware in the chain, or the final handler. | ||
next.ServeHTTP(w, r) | ||
}) | ||
} | ||
|
||
func CallbackHandler(provider OAuth2Provider, jwtSecret string, jwtDuration time.Duration) http.Handler { | ||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
state := r.URL.Query().Get("state") | ||
|
||
dstate, err := base64.StdEncoding.DecodeString(state) | ||
if err != nil { | ||
log.Error(err) | ||
w.WriteHeader(http.StatusInternalServerError) | ||
return | ||
} | ||
|
||
profile, err := provider.GetUserProfile(r) | ||
if err != nil { | ||
log.Error(err) | ||
w.WriteHeader(http.StatusInternalServerError) | ||
return | ||
} | ||
|
||
log.Debugf("Authorized. Redirecting to %s", string(dstate)) | ||
|
||
exp := time.Now().Add(jwtDuration) | ||
jwt, err := IssueJWT(jwtSecret, profile.Email, exp) | ||
http.SetCookie(w, &http.Cookie{ | ||
Name: CookieName, | ||
Value: jwt, | ||
Expires: exp, | ||
Path: "/", | ||
Secure: true, | ||
}) | ||
|
||
http.Redirect(w, r, string(dstate), http.StatusFound) | ||
return | ||
}) | ||
} |
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,21 @@ | ||
package authentication | ||
|
||
import ( | ||
"github.com/dgrijalva/jwt-go" | ||
"time" | ||
) | ||
|
||
func IssueJWT(secret string, email string, expires time.Time) (string, error) { | ||
key := []byte(secret) | ||
|
||
// Create the Claims | ||
claims := &jwt.StandardClaims{ | ||
ExpiresAt: expires.Unix(), | ||
Subject: email, | ||
Issuer: "Helios", | ||
IssuedAt: time.Now().Unix(), | ||
} | ||
|
||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) | ||
return token.SignedString(key) | ||
} |
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
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
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
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
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