2021-10-23 01:12:03 +08:00
package auth
import (
2022-05-27 21:21:14 +08:00
"context"
2021-10-23 01:12:03 +08:00
"encoding/json"
"fmt"
2022-01-07 04:05:38 +08:00
"io"
2021-10-23 01:12:03 +08:00
"net/http"
2021-12-07 04:31:08 +08:00
"github.com/gravitl/netmaker/logger"
2021-10-23 01:12:03 +08:00
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/servercfg"
"golang.org/x/oauth2"
"golang.org/x/oauth2/github"
)
var github_functions = map [ string ] interface { } {
init_provider : initGithub ,
get_user_info : getGithubUserInfo ,
handle_callback : handleGithubCallback ,
handle_login : handleGithubLogin ,
verify_user : verifyGithubUser ,
}
2021-10-23 03:07:45 +08:00
// == handle github authentication here ==
2021-10-23 01:12:03 +08:00
func initGithub ( redirectURL string , clientID string , clientSecret string ) {
auth_provider = & oauth2 . Config {
RedirectURL : redirectURL ,
ClientID : clientID ,
ClientSecret : clientSecret ,
Scopes : [ ] string { } ,
Endpoint : github . Endpoint ,
}
}
func handleGithubLogin ( w http . ResponseWriter , r * http . Request ) {
2022-09-14 03:25:56 +08:00
var oauth_state_string = logic . RandomString ( user_signin_length )
2021-10-23 01:12:03 +08:00
if auth_provider == nil && servercfg . GetFrontendURL ( ) != "" {
2021-11-03 01:25:15 +08:00
http . Redirect ( w , r , servercfg . GetFrontendURL ( ) + "/login?oauth=callback-error" , http . StatusTemporaryRedirect )
2021-10-23 01:12:03 +08:00
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
}
2022-08-04 22:03:39 +08:00
if err := logic . SetState ( oauth_state_string ) ; err != nil {
http . Redirect ( w , r , servercfg . GetFrontendURL ( ) + "/login?oauth=callback-error" , http . StatusTemporaryRedirect )
return
}
2021-10-23 01:12:03 +08:00
var url = auth_provider . AuthCodeURL ( oauth_state_string )
http . Redirect ( w , r , url , http . StatusTemporaryRedirect )
}
func handleGithubCallback ( w http . ResponseWriter , r * http . Request ) {
2022-09-14 03:25:56 +08:00
var rState , rCode = getStateAndCode ( r )
var content , err = getGithubUserInfo ( rState , rCode )
2021-10-23 01:12:03 +08:00
if err != nil {
2021-12-07 04:31:08 +08:00
logger . Log ( 1 , "error when getting user info from github:" , err . Error ( ) )
2021-11-03 01:25:15 +08:00
http . Redirect ( w , r , servercfg . GetFrontendURL ( ) + "/login?oauth=callback-error" , http . StatusTemporaryRedirect )
2021-10-23 01:12:03 +08:00
return
}
_ , err = logic . GetUser ( content . Login )
if err != nil { // user must not exist, so try to make one
if err = addUser ( content . Login ) ; err != nil {
return
}
}
var newPass , fetchErr = fetchPassValue ( "" )
if fetchErr != nil {
return
}
// send a netmaker jwt token
var authRequest = models . UserAuthParams {
UserName : content . Login ,
Password : newPass ,
}
var jwt , jwtErr = logic . VerifyAuthRequest ( authRequest )
if jwtErr != nil {
2021-12-07 04:31:08 +08:00
logger . Log ( 1 , "could not parse jwt for user" , authRequest . UserName )
2021-10-23 01:12:03 +08:00
return
}
2021-12-07 04:31:08 +08:00
logger . Log ( 1 , "completed github OAuth sigin in for" , content . Login )
2021-11-03 01:25:15 +08:00
http . Redirect ( w , r , servercfg . GetFrontendURL ( ) + "/login?login=" + jwt + "&user=" + content . Login , http . StatusPermanentRedirect )
2021-10-23 01:12:03 +08:00
}
2022-09-14 03:25:56 +08:00
func getGithubUserInfo ( state string , code string ) ( * OAuthUser , error ) {
2022-08-04 22:03:39 +08:00
oauth_state_string , isValid := logic . IsStateValid ( state )
2022-09-14 03:25:56 +08:00
if ( ! isValid || state != oauth_state_string ) && ! isStateCached ( state ) {
return nil , fmt . Errorf ( "invalid oauth state" )
2021-10-23 01:12:03 +08:00
}
2022-05-27 21:21:14 +08:00
var token , err = auth_provider . Exchange ( context . Background ( ) , code )
2021-10-23 01:12:03 +08:00
if err != nil {
return nil , fmt . Errorf ( "code exchange failed: %s" , err . Error ( ) )
}
if ! token . Valid ( ) {
return nil , fmt . Errorf ( "GitHub code exchange yielded invalid token" )
}
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 httpClient = & http . Client { }
var httpReq , reqErr = http . NewRequest ( "GET" , "https://api.github.com/user" , nil )
if reqErr != nil {
return nil , fmt . Errorf ( "failed to create request to GitHub" )
}
httpReq . Header . Set ( "Authorization" , "token " + token . AccessToken )
response , err := httpClient . Do ( httpReq )
if err != nil {
return nil , fmt . Errorf ( "failed getting user info: %s" , err . Error ( ) )
}
defer response . Body . Close ( )
2022-01-07 04:05:38 +08:00
contents , err := io . ReadAll ( response . Body )
2021-10-23 01:12:03 +08:00
if err != nil {
return nil , fmt . Errorf ( "failed reading response body: %s" , err . Error ( ) )
}
2022-09-14 03:25:56 +08:00
var userInfo = & OAuthUser { }
2021-10-23 01:12:03 +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 verifyGithubUser ( token * oauth2 . Token ) bool {
return token . Valid ( )
}