Skip to content

Commit

Permalink
Merge pull request #11 from vouch/simongottschlag-adfs_get_username
Browse files Browse the repository at this point in the history
Simongottschlag adfs get username
  • Loading branch information
simongottschlag authored Feb 13, 2019
2 parents 02a50a8 + 8bba8c1 commit 9630d9b
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 4 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ main
config/google_config.json
.vscode/*
vouch
config/config.yml_google
config/config.yml_adfs
config/config.yml_github
config/config.yml_google
config/config.yml_oidc
config/secret
config/config.yml_orig
data/vouch_bolt.db
Expand Down
2 changes: 2 additions & 0 deletions config/config.yml_example
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
# you should probably start with one of the other configs in the example directory
# vouch proxy does a fairly good job of setting its config to sane defaults

# be aware of your indentation, the only top level elements are `vouch` and `oauth`.

vouch:
# logLevel: debug
logLevel: info
Expand Down
72 changes: 71 additions & 1 deletion handlers/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"io/ioutil"
"mime/multipart"
"net/http"
"net/url"
"strconv"
"strings"

log "github.com/Sirupsen/logrus"
Expand Down Expand Up @@ -48,7 +50,7 @@ var (
func randString() string {
b := make([]byte, 32)
rand.Read(b)
return base64.StdEncoding.EncodeToString(b)
return base64.URLEncoding.EncodeToString(b)
}

func loginURL(r *http.Request, state string) string {
Expand Down Expand Up @@ -424,6 +426,8 @@ func getUserInfo(r *http.Request, user *structs.User) 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 {
return getUserInfoFromIndieAuth(r, user)
} else if cfg.GenOAuth.Provider == cfg.Providers.ADFS {
return getUserInfoFromADFS(r, user)
}

providerToken, err := cfg.OAuthClient.Exchange(oauth2.NoContext, r.URL.Query().Get("code"))
Expand Down Expand Up @@ -569,6 +573,72 @@ func getUserInfoFromIndieAuth(r *http.Request, user *structs.User) error {
return nil
}

type adfsTokenRes struct {
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
IDToken string `json:"id_token"`
ExpiresIn int64 `json:"expires_in"` // relative seconds from now
}

// 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 {
code := r.URL.Query().Get("code")
log.Errorf("code: %s", code)

formData := url.Values{}
formData.Set("code", code)
formData.Set("grant_type", "authorization_code")
formData.Set("resource", cfg.GenOAuth.RedirectURL)
formData.Set("client_id", cfg.GenOAuth.ClientID)
formData.Set("redirect_uri", cfg.GenOAuth.RedirectURL)
formData.Set("client_secret", cfg.GenOAuth.ClientSecret)

req, err := http.NewRequest("POST", cfg.GenOAuth.TokenURL, strings.NewReader(formData.Encode()))
if err != nil {
return err
}
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
req.Header.Add("Content-Length", strconv.Itoa(len(formData.Encode())))
req.Header.Set("Accept", "application/json")

client := &http.Client{}
userinfo, err := client.Do(req)

if err != nil {
return err
}
defer userinfo.Body.Close()

body, _ := ioutil.ReadAll(userinfo.Body)
tokenRes := adfsTokenRes{}

if err := json.Unmarshal(body, &tokenRes); err != nil {
log.Errorf("oauth2: cannot fetch token: %v", err)
return nil
}

s := strings.Split(tokenRes.IDToken, ".")
if len(s) < 2 {
log.Error("jws: invalid token received")
return nil
}

idToken, err := base64.RawURLEncoding.DecodeString(s[1])
if err != nil {
log.Error(err)
return nil
}

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

adfsUser.PrepareUserData()
user.Username = adfsUser.Username
log.Debug(user)
return nil
}

// the standard error
// this is captured by nginx, which converts the 401 into 302 to the login page
func error401(w http.ResponseWriter, r *http.Request, ae AuthError) {
Expand Down
14 changes: 12 additions & 2 deletions pkg/cfg/cfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ type OAuthProviders struct {
Google string
GitHub string
IndieAuth string
ADFS string
OIDC string
}

Expand Down Expand Up @@ -113,6 +114,7 @@ var (
Google: "google",
GitHub: "github",
IndieAuth: "indieauth",
ADFS: "adfs",
OIDC: "oidc",
}

Expand Down Expand Up @@ -236,8 +238,8 @@ func BasicTest() error {
case GenOAuth.Provider != Providers.Google && GenOAuth.AuthURL == "":
// everyone except IndieAuth and Google has an authURL
return errors.New("configuration error: oauth.auth_url not found")
case GenOAuth.Provider != Providers.Google && GenOAuth.Provider != Providers.IndieAuth && GenOAuth.UserInfoURL == "":
// everyone except IndieAuth and Google has an userInfoURL
case GenOAuth.Provider != Providers.Google && GenOAuth.Provider != Providers.IndieAuth && GenOAuth.Provider != Providers.ADFS && GenOAuth.UserInfoURL == "":
// everyone except IndieAuth, Google and ADFS has an userInfoURL
return errors.New("configuration error: oauth.user_info_url not found")
}

Expand Down Expand Up @@ -404,6 +406,9 @@ func setDefaults() {
} else if GenOAuth.Provider == Providers.GitHub {
setDefaultsGitHub()
configureOAuthClient()
} else if GenOAuth.Provider == Providers.ADFS {
setDefaultsADFS()
configureOAuthClient()
} else {
configureOAuthClient()
}
Expand All @@ -429,6 +434,11 @@ func setDefaultsGoogle() {
}
}

func setDefaultsADFS() {
log.Info("configuring ADFS OAuth")
OAuthopts = oauth2.SetAuthURLParam("resource", GenOAuth.RedirectURL) // Needed or all claims won't be included
}

func setDefaultsGitHub() {
// log.Info("configuring GitHub OAuth")
if GenOAuth.AuthURL == "" {
Expand Down
16 changes: 16 additions & 0 deletions pkg/structs/structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,22 @@ func (u *GoogleUser) PrepareUserData() {
u.Username = u.Email
}

type ADFSUser struct {
User
Sub string `json:"sub"`
UPN string `json:"upn"`
// UniqueName string `json:"unique_name"`
// PwdExp string `json:"pwd_exp"`
// SID string `json:"sid"`
// Groups string `json:"groups"`
// jwt.StandardClaims
}

// PrepareUserData implement PersonalData interface
func (u *ADFSUser) PrepareUserData() {
u.Username = u.UPN
}

// GitHubUser is a retrieved and authentiacted user from GitHub.
type GitHubUser struct {
User
Expand Down

0 comments on commit 9630d9b

Please sign in to comment.