github.com/spinnaker/spin@v1.30.0/config/auth/iap/user_iap_auth_helper.go (about)

     1  // Copyright (c) 2018, Snap Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //   http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  //   Unless required by applicable law or agreed to in writing, software
    10  //   distributed under the License is distributed on an "AS IS" BASIS,
    11  //   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  //   See the License for the specific language governing permissions and
    13  //   limitations under the License.
    14  
    15  package config
    16  
    17  import (
    18  	"crypto/rand"
    19  	"encoding/base64"
    20  	"fmt"
    21  	"net"
    22  	"net/http"
    23  	"net/url"
    24  
    25  	"golang.org/x/oauth2"
    26  
    27  	"github.com/spinnaker/spin/util/execcmd"
    28  )
    29  
    30  const (
    31  	googleOauthHost         = "accounts.google.com"
    32  	googleOauthPath         = "/o/oauth2/v2/auth"
    33  	googleOauthSchema       = "https"
    34  	googleOauthResponseType = "code"
    35  	googleOauthScope        = "openid email"
    36  )
    37  
    38  // returns the token get from google for IAP
    39  func GetIapToken(iapConfig Config) (string, error) {
    40  	if iapConfig.IapIdToken != "" {
    41  		return iapConfig.IapIdToken, nil
    42  	}
    43  
    44  	if iapConfig.ServiceAccountKeyPath != "" && iapConfig.IapClientId != "" {
    45  		return GetIDTokenWithServiceAccount(iapConfig)
    46  	}
    47  
    48  	if iapConfig.IapClientRefresh == "" {
    49  		return userInteract(iapConfig)
    50  	}
    51  
    52  	return RequestIapIDToken(iapConfig.IapClientRefresh,
    53  		iapConfig.OAuthClientId,
    54  		iapConfig.OAuthClientSecret,
    55  		iapConfig.IapClientId)
    56  }
    57  
    58  // userInteract lets the spin user fetch a token.
    59  func userInteract(cfg Config) (string, error) {
    60  	listener, err := net.Listen("tcp", "127.0.0.1:0")
    61  	if err != nil {
    62  		return "", err
    63  	}
    64  
    65  	port := listener.Addr().(*net.TCPAddr).Port
    66  	clientStateToken := make([]byte, serverStateTokenLen)
    67  	if _, err = rand.Read(clientStateToken); err != nil {
    68  		return "", err
    69  	}
    70  
    71  	clientState := base64.URLEncoding.EncodeToString(clientStateToken)
    72  	accessToken := make(chan string)
    73  
    74  	rcv := &oauthReceiver{
    75  		port:        port,
    76  		clientState: clientState,
    77  		doneChan:    make(chan error),
    78  		callback: func(token *oauth2.Token, config *oauth2.Config, s2 string) (string, error) {
    79  			iapToken, err := RequestIapIDToken(token.AccessToken,
    80  				cfg.OAuthClientId,
    81  				cfg.OAuthClientSecret,
    82  				cfg.IapClientId)
    83  			if err != nil {
    84  				close(accessToken)
    85  				return "", err
    86  			}
    87  			accessToken <- iapToken
    88  			return "", nil
    89  		},
    90  		clientId:     cfg.OAuthClientId,
    91  		clientSecret: cfg.OAuthClientSecret,
    92  	}
    93  
    94  	srv := http.Server{Addr: listener.Addr().String(), Handler: rcv, ConnState: rcv.killWhenReady}
    95  	go srv.Serve(listener)
    96  
    97  	url := oauthURL(cfg.OAuthClientId, clientState, port)
    98  
    99  	resStr := fmt.Sprintf("Your browser has been opened to visit:\n%s\n\n", url)
   100  	if err = execcmd.OpenUrl(url); err != nil {
   101  		resStr = fmt.Sprintf("Follow this link in your browser:\n%s\n\n", url)
   102  	}
   103  	fmt.Println(resStr)
   104  
   105  	return <-accessToken, nil
   106  }
   107  
   108  func oauthURL(clientId string, clientState string, port int) string {
   109  	oauthUrl := url.URL{
   110  		Scheme: googleOauthSchema,
   111  		Host:   googleOauthHost,
   112  		Path:   googleOauthPath,
   113  	}
   114  
   115  	q := oauthUrl.Query()
   116  	q.Add("client_id", clientId)
   117  	q.Add("state", clientState)
   118  	q.Add("response_type", googleOauthResponseType)
   119  	q.Add("scope", googleOauthScope)
   120  	q.Add("redirect_uri", fmt.Sprintf("http://localhost:%d", port))
   121  	oauthUrl.RawQuery = q.Encode()
   122  
   123  	return oauthUrl.String()
   124  }