From f39e16fbc9592b139029183ea57c68c8f0db528c Mon Sep 17 00:00:00 2001 From: 0xdcarns Date: Fri, 22 Oct 2021 15:07:45 -0400 Subject: [PATCH] added azure sign in --- auth/auth.go | 2 +- auth/azure-ad.go | 126 +++++++++++++++++++++++++++++++++++++++++++++++ auth/github.go | 8 +-- auth/google.go | 6 +-- 4 files changed, 134 insertions(+), 8 deletions(-) create mode 100644 auth/azure-ad.go diff --git a/auth/auth.go b/auth/auth.go index f6fa331d..eb2b0f91 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -35,7 +35,7 @@ func getCurrentAuthFunctions() map[string]interface{} { case google_provider_name: return google_functions case azure_ad_provider_name: - return google_functions + return azure_ad_functions case github_provider_name: return github_functions default: diff --git a/auth/azure-ad.go b/auth/azure-ad.go new file mode 100644 index 00000000..bb67b719 --- /dev/null +++ b/auth/azure-ad.go @@ -0,0 +1,126 @@ +package auth + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "os" + + "github.com/gravitl/netmaker/logic" + "github.com/gravitl/netmaker/models" + "github.com/gravitl/netmaker/servercfg" + "golang.org/x/oauth2" + "golang.org/x/oauth2/microsoft" +) + +var azure_ad_functions = map[string]interface{}{ + init_provider: initAzureAD, + get_user_info: getAzureUserInfo, + handle_callback: handleAzureCallback, + handle_login: handleAzureLogin, + verify_user: verifyAzureUser, +} + +type azureOauthUser struct { + UserPrincipalName string `json:"userPrincipalName" bson:"userPrincipalName"` + AccessToken string `json:"accesstoken" bson:"accesstoken"` +} + +// == handle azure ad authentication here == + +func initAzureAD(redirectURL string, clientID string, clientSecret string) { + auth_provider = &oauth2.Config{ + RedirectURL: redirectURL, + ClientID: clientID, + ClientSecret: clientSecret, + Scopes: []string{"User.Read"}, + Endpoint: microsoft.AzureADEndpoint(os.Getenv("AZURE_TENANT")), + } +} + +func handleAzureLogin(w http.ResponseWriter, r *http.Request) { + oauth_state_string = logic.RandomString(16) + if auth_provider == nil && servercfg.GetFrontendURL() != "" { + http.Redirect(w, r, servercfg.GetFrontendURL()+"?oauth=callback-error", http.StatusTemporaryRedirect) + return + } else if auth_provider == nil { + fmt.Fprintf(w, "%s", []byte("no frontend URL was provided and an OAuth login was attempted\nplease reconfigure server to use OAuth or use basic credentials")) + return + } + var url = auth_provider.AuthCodeURL(oauth_state_string) + http.Redirect(w, r, url, http.StatusTemporaryRedirect) +} + +func handleAzureCallback(w http.ResponseWriter, r *http.Request) { + + var content, err = getAzureUserInfo(r.FormValue("state"), r.FormValue("code")) + if err != nil { + logic.Log("error when getting user info from azure: "+err.Error(), 1) + http.Redirect(w, r, servercfg.GetFrontendURL()+"?oauth=callback-error", http.StatusTemporaryRedirect) + return + } + _, err = logic.GetUser(content.UserPrincipalName) + if err != nil { // user must not exists, so try to make one + if err = addUser(content.UserPrincipalName); err != nil { + return + } + } + var newPass, fetchErr = fetchPassValue("") + if fetchErr != nil { + return + } + // send a netmaker jwt token + var authRequest = models.UserAuthParams{ + UserName: content.UserPrincipalName, + Password: newPass, + } + + var jwt, jwtErr = logic.VerifyAuthRequest(authRequest) + if jwtErr != nil { + logic.Log("could not parse jwt for user "+authRequest.UserName, 1) + return + } + + logic.Log("completed azure OAuth sigin in for "+content.UserPrincipalName, 1) + http.Redirect(w, r, servercfg.GetFrontendURL()+"?login="+jwt+"&user="+content.UserPrincipalName, http.StatusPermanentRedirect) +} + +func getAzureUserInfo(state string, code string) (*azureOauthUser, error) { + if state != oauth_state_string { + return nil, fmt.Errorf("invalid oauth state") + } + var token, err = auth_provider.Exchange(oauth2.NoContext, code) + if err != nil { + return nil, fmt.Errorf("code exchange failed: %s", err.Error()) + } + var data []byte + data, err = json.Marshal(token) + if err != nil { + return nil, fmt.Errorf("failed to convert token to json: %s", err.Error()) + } + var httpReq, reqErr = http.NewRequest("GET", "https://graph.microsoft.com/v1.0/me", nil) + if reqErr != nil { + return nil, fmt.Errorf("failed to create request to GitHub") + } + httpReq.Header.Set("Authorization", "Bearer "+token.AccessToken) + response, err := http.DefaultClient.Do(httpReq) + if err != nil { + return nil, fmt.Errorf("failed getting user info: %s", err.Error()) + } + defer response.Body.Close() + contents, err := ioutil.ReadAll(response.Body) + if err != nil { + return nil, fmt.Errorf("failed reading response body: %s", err.Error()) + } + var userInfo = &azureOauthUser{} + if err = json.Unmarshal(contents, userInfo); err != nil { + return nil, fmt.Errorf("failed parsing email from response data: %s", err.Error()) + } + userInfo.AccessToken = string(data) + return userInfo, nil +} + +func verifyAzureUser(token *oauth2.Token) bool { + return token.Valid() +} diff --git a/auth/github.go b/auth/github.go index a8d0a371..552f2525 100644 --- a/auth/github.go +++ b/auth/github.go @@ -26,7 +26,7 @@ type githubOauthUser struct { AccessToken string `json:"accesstoken" bson:"accesstoken"` } -// == handle google authentication here == +// == handle github authentication here == func initGithub(redirectURL string, clientID string, clientSecret string) { auth_provider = &oauth2.Config{ @@ -81,13 +81,13 @@ func handleGithubCallback(w http.ResponseWriter, r *http.Request) { return } - logic.Log("completed github oauth sigin in for "+content.Login, 0) - http.Redirect(w, r, servercfg.GetFrontendURL()+"?login="+jwt+"&email="+content.Login, http.StatusPermanentRedirect) + logic.Log("completed github OAuth sigin in for "+content.Login, 1) + http.Redirect(w, r, servercfg.GetFrontendURL()+"?login="+jwt+"&user="+content.Login, http.StatusPermanentRedirect) } func getGithubUserInfo(state string, code string) (*githubOauthUser, error) { if state != oauth_state_string { - return nil, fmt.Errorf("invalid oauth state") + return nil, fmt.Errorf("invalid OAuth state") } var token, err = auth_provider.Exchange(oauth2.NoContext, code) if err != nil { diff --git a/auth/google.go b/auth/google.go index 0a5d8a26..91bb8030 100644 --- a/auth/google.go +++ b/auth/google.go @@ -81,13 +81,13 @@ func handleGoogleCallback(w http.ResponseWriter, r *http.Request) { return } - logic.Log("completed google oauth sigin in for "+content.Email, 0) - http.Redirect(w, r, servercfg.GetFrontendURL()+"?login="+jwt+"&email="+content.Email, http.StatusPermanentRedirect) + logic.Log("completed google OAuth sigin in for "+content.Email, 1) + http.Redirect(w, r, servercfg.GetFrontendURL()+"?login="+jwt+"&user="+content.Email, http.StatusPermanentRedirect) } func getGoogleUserInfo(state string, code string) (*googleOauthUser, error) { if state != oauth_state_string { - return nil, fmt.Errorf("invalid oauth state") + return nil, fmt.Errorf("invalid OAuth state") } var token, err = auth_provider.Exchange(oauth2.NoContext, code) if err != nil {