2021-10-21 02:17:31 +08:00
package auth
import (
2021-10-22 03:28:58 +08:00
"encoding/json"
2021-10-21 02:17:31 +08:00
"fmt"
"io/ioutil"
"net/http"
2021-10-22 03:28:58 +08:00
"github.com/gravitl/netmaker/logic"
2021-10-22 08:32:23 +08:00
"github.com/gravitl/netmaker/models"
2021-10-22 03:28:58 +08:00
"github.com/gravitl/netmaker/servercfg"
2021-10-21 02:17:31 +08:00
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
)
var google_functions = map [ string ] interface { } {
init_provider : initGoogle ,
2021-10-23 01:12:03 +08:00
get_user_info : getGoogleUserInfo ,
2021-10-21 02:17:31 +08:00
handle_callback : handleGoogleCallback ,
handle_login : handleGoogleLogin ,
2021-10-22 03:28:58 +08:00
verify_user : verifyGoogleUser ,
2021-10-21 02:17:31 +08:00
}
2021-10-23 01:12:03 +08:00
type googleOauthUser struct {
Email string ` json:"email" bson:"email" `
AccessToken string ` json:"accesstoken" bson:"accesstoken" `
}
2021-10-21 02:17:31 +08:00
// == handle google authentication here ==
func initGoogle ( redirectURL string , clientID string , clientSecret string ) {
auth_provider = & oauth2 . Config {
RedirectURL : redirectURL ,
ClientID : clientID ,
ClientSecret : clientSecret ,
Scopes : [ ] string { "https://www.googleapis.com/auth/userinfo.email" } ,
Endpoint : google . Endpoint ,
}
}
func handleGoogleLogin ( w http . ResponseWriter , r * http . Request ) {
2021-10-22 03:28:58 +08:00
oauth_state_string = logic . RandomString ( 16 )
2021-10-22 21:47:29 +08:00
if auth_provider == nil && servercfg . GetFrontendURL ( ) != "" {
http . Redirect ( w , r , servercfg . GetFrontendURL ( ) + "?oauth=callback-error" , http . StatusTemporaryRedirect )
2021-10-23 01:12:03 +08:00
return
2021-10-22 21:47:29 +08:00
} 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
}
2021-10-22 08:32:23 +08:00
var url = auth_provider . AuthCodeURL ( oauth_state_string )
2021-10-21 02:17:31 +08:00
http . Redirect ( w , r , url , http . StatusTemporaryRedirect )
}
func handleGoogleCallback ( w http . ResponseWriter , r * http . Request ) {
2021-10-23 01:12:03 +08:00
var content , err = getGoogleUserInfo ( r . FormValue ( "state" ) , r . FormValue ( "code" ) )
2021-10-21 02:17:31 +08:00
if err != nil {
2021-10-23 01:12:03 +08:00
logic . Log ( "error when getting user info from google: " + err . Error ( ) , 1 )
2021-10-22 03:28:58 +08:00
http . Redirect ( w , r , servercfg . GetFrontendURL ( ) + "?oauth=callback-error" , http . StatusTemporaryRedirect )
2021-10-21 02:17:31 +08:00
return
}
2021-10-22 08:32:23 +08:00
_ , err = logic . GetUser ( content . Email )
if err != nil { // user must not exists, so try to make one
if err = addUser ( content . Email ) ; err != nil {
return
}
}
var newPass , fetchErr = fetchPassValue ( "" )
if fetchErr != nil {
return
}
// send a netmaker jwt token
var authRequest = models . UserAuthParams {
UserName : content . Email ,
Password : newPass ,
}
var jwt , jwtErr = logic . VerifyAuthRequest ( authRequest )
if jwtErr != nil {
logic . Log ( "could not parse jwt for user " + authRequest . UserName , 1 )
return
}
2021-10-23 03:07:45 +08:00
logic . Log ( "completed google OAuth sigin in for " + content . Email , 1 )
http . Redirect ( w , r , servercfg . GetFrontendURL ( ) + "?login=" + jwt + "&user=" + content . Email , http . StatusPermanentRedirect )
2021-10-21 02:17:31 +08:00
}
2021-10-23 01:12:03 +08:00
func getGoogleUserInfo ( state string , code string ) ( * googleOauthUser , error ) {
2021-10-21 02:17:31 +08:00
if state != oauth_state_string {
2021-10-23 03:07:45 +08:00
return nil , fmt . Errorf ( "invalid OAuth state" )
2021-10-21 02:17:31 +08:00
}
2021-10-22 03:28:58 +08:00
var token , err = auth_provider . Exchange ( oauth2 . NoContext , code )
2021-10-21 02:17:31 +08:00
if err != nil {
return nil , fmt . Errorf ( "code exchange failed: %s" , err . Error ( ) )
}
2021-10-22 03:28:58 +08:00
var data [ ] byte
data , err = json . Marshal ( token )
if err != nil {
return nil , fmt . Errorf ( "failed to convert token to json: %s" , err . Error ( ) )
}
2021-10-21 02:17:31 +08:00
response , err := http . Get ( "https://www.googleapis.com/oauth2/v2/userinfo?access_token=" + token . AccessToken )
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 ( ) )
}
2021-10-23 01:12:03 +08:00
var userInfo = & googleOauthUser { }
2021-10-22 03:28:58 +08:00
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 verifyGoogleUser ( token * oauth2 . Token ) bool {
2021-10-22 21:47:29 +08:00
return token . Valid ( )
2021-10-21 02:17:31 +08:00
}