github.com/openshift-online/ocm-sdk-go@v0.1.473/authentication/device_auth.go (about)

     1  package authentication
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"golang.org/x/oauth2"
     8  )
     9  
    10  const (
    11  	DeviceAuthURL = "https://sso.redhat.com/auth/realms/redhat-external/protocol/openid-connect/auth/device"
    12  )
    13  
    14  type DeviceAuthConfig struct {
    15  	conf               *oauth2.Config
    16  	verifierOpt        oauth2.AuthCodeOption
    17  	DeviceAuthResponse *oauth2.DeviceAuthResponse
    18  	ClientID           string
    19  }
    20  
    21  // Step 1:
    22  // Initiates device code flow and returns the device auth config.
    23  // After running, use your DeviceAuthConfig to display the user code and verification URI
    24  //
    25  //	fmt.Printf("To continue login, navigate to %v and enter code %v\n", deviceAuthResp.VerificationURI, deviceAuthResp.UserCode)
    26  //	fmt.Printf("Checking status every %v seconds...\n", deviceAuthResp.Interval)
    27  func (d *DeviceAuthConfig) InitiateDeviceAuth(ctx context.Context) (*DeviceAuthConfig, error) {
    28  	d.conf = &oauth2.Config{
    29  		ClientID:     d.ClientID,
    30  		ClientSecret: "",
    31  		Scopes:       []string{"openid"},
    32  		Endpoint: oauth2.Endpoint{
    33  			DeviceAuthURL: DeviceAuthURL,
    34  			TokenURL:      DefaultTokenURL,
    35  		},
    36  	}
    37  
    38  	// Verifiers and Challenges are required for device auth
    39  	verifier := oauth2.GenerateVerifier()
    40  	verifierOpt := oauth2.VerifierOption(verifier)
    41  	challenge := oauth2.S256ChallengeOption(verifier)
    42  
    43  	// Get device code
    44  	deviceAuthResp, err := d.conf.DeviceAuth(ctx, challenge, verifierOpt)
    45  	if err != nil {
    46  		return d, fmt.Errorf("failed to get device code: %v", err)
    47  	}
    48  
    49  	d.DeviceAuthResponse = deviceAuthResp
    50  	d.verifierOpt = verifierOpt
    51  
    52  	return d, nil
    53  }
    54  
    55  // Step 2:
    56  // Initiates polling for token exchange and returns a refresh token
    57  func (d *DeviceAuthConfig) PollForTokenExchange(ctx context.Context) (string, error) {
    58  	if d.DeviceAuthResponse == nil || d.verifierOpt == nil {
    59  		return "", fmt.Errorf("required config is nil, please run InitiateDeviceAuth first")
    60  	}
    61  	// Wait for the user to enter the code, polls at interval specified in deviceAuthResp.Interval
    62  	token, err := d.conf.DeviceAccessToken(ctx, d.DeviceAuthResponse, d.verifierOpt)
    63  	if err != nil {
    64  		return "", fmt.Errorf("error exchanging for token: %v", err)
    65  	}
    66  
    67  	return token.RefreshToken, nil
    68  }