Skip to content

Commit

Permalink
Add support for grabbing claims from the return of getuserinfo functi…
Browse files Browse the repository at this point in the history
…ons for all providers.
  • Loading branch information
artagel authored and bnfinet committed May 2, 2019
1 parent aa2a4a2 commit e24bd02
Showing 1 changed file with 60 additions and 11 deletions.
71 changes: 60 additions & 11 deletions handlers/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ const (
base64Bytes = 32
)

// Temporary struct storing custom claims until JWT creation.
var CustomClaims map[string]interface{}

var (
// Templates
indexTemplate = template.Must(template.ParseFiles("./templates/index.tmpl"))
Expand Down Expand Up @@ -398,7 +401,9 @@ func CallbackHandler(w http.ResponseWriter, r *http.Request) {
}

user := structs.User{}
if err := getUserInfo(r, &user); err != nil {
customClaims := CustomClaims

if err := getUserInfo(r, &user, customClaims); err != nil {
log.Error(err)
http.Error(w, err.Error(), http.StatusBadRequest)
return
Expand Down Expand Up @@ -438,7 +443,7 @@ func CallbackHandler(w http.ResponseWriter, r *http.Request) {

// TODO: put all getUserInfo logic into its own pkg

func getUserInfo(r *http.Request, user *structs.User) error {
func getUserInfo(r *http.Request, user *structs.User, customClaims map[string]interface{}) error {

// indieauth sends the "me" setting in json back to the callback, so just pluck it from the callback
if cfg.GenOAuth.Provider == cfg.Providers.IndieAuth {
Expand All @@ -459,20 +464,24 @@ func getUserInfo(r *http.Request, user *structs.User) error {
} else if cfg.GenOAuth.Provider == cfg.Providers.GitHub {
return getUserInfoFromGitHub(client, user, providerToken)
} else if cfg.GenOAuth.Provider == cfg.Providers.OIDC {
return getUserInfoFromOpenID(client, user, providerToken)
return getUserInfoFromOpenID(client, user, customClaims, providerToken)
}
log.Error("we don't know how to look up the user info")
return nil
}

func getUserInfoFromOpenID(client *http.Client, user *structs.User, ptoken *oauth2.Token) error {
func getUserInfoFromOpenID(client *http.Client, user *structs.User, customClaims map[string]interface{}, ptoken *oauth2.Token) error {
userinfo, err := client.Get(cfg.GenOAuth.UserInfoURL)
if err != nil {
return err
}
defer userinfo.Body.Close()
data, _ := ioutil.ReadAll(userinfo.Body)
log.Infof("OpenID userinfo body: ", string(data))
if err = mapClaims(data, customClaims); err != nil {
log.Error(err)
return err
}
if err = json.Unmarshal(data, user); err != nil {
log.Error(err)
return err
Expand All @@ -481,14 +490,18 @@ func getUserInfoFromOpenID(client *http.Client, user *structs.User, ptoken *oaut
return nil
}

func getUserInfoFromGoogle(client *http.Client, user *structs.User) error {
func getUserInfoFromGoogle(client *http.Client, user *structs.User, customClaims map[string]interface{}) error {
userinfo, err := client.Get(cfg.GenOAuth.UserInfoURL)
if err != nil {
return err
}
defer userinfo.Body.Close()
data, _ := ioutil.ReadAll(userinfo.Body)
log.Infof("google userinfo body: ", string(data))
if err = mapClaims(data, customClaims); err != nil {
log.Error(err)
return err
}
if err = json.Unmarshal(data, user); err != nil {
log.Error(err)
return err
Expand All @@ -500,7 +513,7 @@ func getUserInfoFromGoogle(client *http.Client, user *structs.User) error {

// github
// https://developer.github.com/apps/building-integrations/setting-up-and-registering-oauth-apps/about-authorization-options-for-oauth-apps/
func getUserInfoFromGitHub(client *http.Client, user *structs.User, ptoken *oauth2.Token) error {
func getUserInfoFromGitHub(client *http.Client, user *structs.User, ptoken *oauth2.Token, customClaims map[string]interface{}) error {

log.Errorf("ptoken.AccessToken: %s", ptoken.AccessToken)
userinfo, err := client.Get(cfg.GenOAuth.UserInfoURL + ptoken.AccessToken)
Expand All @@ -511,6 +524,10 @@ func getUserInfoFromGitHub(client *http.Client, user *structs.User, ptoken *oaut
defer userinfo.Body.Close()
data, _ := ioutil.ReadAll(userinfo.Body)
log.Infof("github userinfo body: ", string(data))
if err = mapClaims(data, customClaims); err != nil {
log.Error(err)
return err
}
ghUser := structs.GitHubUser{}
if err = json.Unmarshal(data, &ghUser); err != nil {
log.Error(err)
Expand All @@ -533,7 +550,7 @@ func getUserInfoFromGitHub(client *http.Client, user *structs.User, ptoken *oaut
return nil
}

func getUserInfoFromIndieAuth(r *http.Request, user *structs.User) error {
func getUserInfoFromIndieAuth(r *http.Request, user *structs.User, customClaims map[string]interface{}) error {

code := r.URL.Query().Get("code")
log.Errorf("ptoken.AccessToken: %s", code)
Expand Down Expand Up @@ -579,6 +596,10 @@ func getUserInfoFromIndieAuth(r *http.Request, user *structs.User) error {
defer userinfo.Body.Close()
data, _ := ioutil.ReadAll(userinfo.Body)
log.Infof("indieauth userinfo body: ", string(data))
if err = mapClaims(data, customClaims); err != nil {
log.Error(err)
return err
}
iaUser := structs.IndieAuthUser{}
if err = json.Unmarshal(data, &iaUser); err != nil {
log.Error(err)
Expand All @@ -598,7 +619,7 @@ type adfsTokenRes struct {
}

// More info: https://docs.microsoft.com/en-us/windows-server/identity/ad-fs/overview/ad-fs-scenarios-for-developers#supported-scenarios
func getUserInfoFromADFS(r *http.Request, user *structs.User) error {
func getUserInfoFromADFS(r *http.Request, user *structs.User, customClaims map[string]interface{}) error {
code := r.URL.Query().Get("code")
log.Errorf("code: %s", code)

Expand Down Expand Up @@ -648,11 +669,15 @@ func getUserInfoFromADFS(r *http.Request, user *structs.User) error {

adfsUser := structs.ADFSUser{}
json.Unmarshal([]byte(idToken), &adfsUser)
log.Infof("adfs adfsUser: ", adfsUser)

log.Infof("adfs adfsUser: %+v", adfsUser)
if err = mapClaims([]byte(idToken), user); err != nil {

This comment has been minimized.

Copy link
@simongottschlag

simongottschlag Sep 9, 2019

Contributor

@artagel do you know why mapClaims uses []byte(idToken) and not body (or data in #147)?

This comment has been minimized.

Copy link
@artagel

artagel Sep 9, 2019

Author Contributor

I think that may be an error from when I was making the changes across the board. It certainly seems like it should be body.

This comment has been minimized.

Copy link
@simongottschlag

simongottschlag Sep 10, 2019

Contributor

@artagel Ok! I'll update #147 with data.

This comment has been minimized.

Copy link
@twelve34

twelve34 Oct 9, 2019

@artagel i'm testen ADFS using custom claims and the customclaims are replyed into de Access-token from ADFS Why you use data which is the complete reply. mapClaims() didn't find any customclaims which are inside Access-token or idToken as part of data

This comment has been minimized.

Copy link
@simongottschlag

simongottschlag Oct 9, 2019

Contributor

@twelve34 I'm not using the customClaims myself. I think you should open up the idToken and map them instead of how it's done right now - but I haven't had time to dig into it.

This comment has been minimized.

Copy link
@artagel

artagel Oct 9, 2019

Author Contributor

@twelve34, Can you open an issue and describe the problem a little clearer?

log.Error(err)
return err
}
adfsUser.PrepareUserData()
user.Username = adfsUser.Username
log.Debug(user)
user.Email = adfsUser.Email
log.Debugf("User Obj: %+v", user)
return nil
}

Expand Down Expand Up @@ -686,3 +711,27 @@ func ok200(w http.ResponseWriter, r *http.Request) {
log.Error(err)
}
}

func mapClaims(claims []byte, customClaims map[string]interface{}) error {
// Create a struct that contains the claims that we want to store from the config.
var f interface{}
err := json.Unmarshal(claims, &f)
if err != nil {
log.Error("Error unmarshaling claims")
return err
}
m := f.(map[string]interface{})
for k, _ := range m {
var found = false
for _, e := range cfg.Cfg.Headers.Claims {
if k == e {
found = true
}
}
if found == false {
delete(m, k)
}
}
customClaims = m
return nil
}

0 comments on commit e24bd02

Please sign in to comment.