Skip to content

Supported Scenarios

Hemanth Chittanuru edited this page Aug 6, 2020 · 1 revision

Username and Password

Username and Password token acquisition flow has some restrictions, and this is detailed here

func tryUsernamePassword() {
    userNameParams := msalgo.CreateAcquireTokenUsernamePasswordParameters(config.Scopes, config.Username, config.Password)
	result, err := publicClientApp.AcquireTokenByUsernamePassword(userNameParams)
	if err != nil {
		log.Fatal(err)
	}
	accessToken := result.GetAccessToken()
	log.Info("Access token is: " + accessToken)
}

//config has all the values required for token acquisition
publicClientApp, err := publicClientApp, err := msalgo.CreatePublicClientApplication(config.ClientID, config.Authority)
if err != nil {
	log.Fatal(err)
}
//Checking to see if there is an account with the same username in the cache
var userAccount msalgo.AccountInterfacer
accounts := publicClientApp.GetAccounts()
for _, account := range accounts {
    if account.GetUsername() == config.Username {
        userAccount = account
    }
}
//If an account with the same username exists, try to acquire the token silently
if userAccount != nil {
    silentParams := msalgo.CreateAcquireTokenSilentParametersWithAccount(config.Scopes, userAccount)
    result, err := publicClientApp.AcquireTokenSilent(silentParams)
    if err != nil {
        //If this is not possible, try username-password flow
        tryUsernamePassword()
    } else {
        accessToken := result.GetAccessToken()
	    log.Info("Access token is: " + accessToken)
    }
} else {
    //If the account doesn't exist, try username-password flow
    tryUsernamePassword()
}

Device Code Flow

func deviceCodeCallback(deviceCodeResult msalgo.IDeviceCodeResult) {
	log.Infof(deviceCodeResult.GetMessage())
}

func tryDeviceCodeFlow(publicClientApp *msalgo.PublicClientApplication) {
    // This cancel timeout can be set by the user to cancel the device code request after a certain interval
    cancelTimeout := 100 
	cancelCtx, cancelFunc := context.WithTimeout(context.Background(), time.Duration(cancelTimeout)*time.Second)
    defer cancelFunc()
    // The device code callback is used to print the device code message to the user
    deviceCodeParams := msalgo.CreateAcquireTokenDeviceCodeParameters(cancelCtx, config.Scopes, deviceCodeCallback)
    // The channels are used as there is user interaction required (entering the device code at the verification URL)
	resultChannel := make(chan msalgo.AuthenticationResultInterfacer)
	errChannel := make(chan error)
	go func() {
		result, err := publicClientApp.AcquireTokenByDeviceCode(deviceCodeParams)
		errChannel <- err
		resultChannel <- result
	}()
	err = <-errChannel
	if err != nil {
		log.Fatal(err)
	}
	result := <-resultChannel
	fmt.Println("Access token is " + result.GetAccessToken())
}

//config has all the values required for token acquisition
publicClientApp, err := msalgo.CreatePublicClientApplication(config.ClientID, config.Authority)
if err != nil {
    log.Fatal(err)
}
//Try silent token acquistion, but this can be skipped
//Assuming the account we want is the first one
var userAccount msalgo.AccountInterfacer
accounts := publicClientApp.GetAccounts()
if len(accounts) == 0 {
    userAccount = nil
} else {
    userAccount = accounts[0]
}
//If the account doesn't exist, try device code flow
if userAccount == nil {
    tryDeviceCodeFlow(publicClientApp)
} else {
    silentParams := msalgo.CreateAcquireTokenSilentParametersWithAccount(config.Scopes, userAccount)
    result, err := publicClientApp.AcquireTokenSilent(silentParams)
    //If there is an error with silent acquisition, try device code flow
    if err != nil {
        log.Info(err)
        tryDeviceCodeFlow(publicClientApp)
    } else {
        fmt.Println("Access token is " + result.GetAccessToken())
    }
}

Interactive

MSAL Go doesn't provide an interactive acquire token method directly. Instead, it requires the application to send an authorization request in its implementation of the user interaction flow to obtain an authorization code. This code can then be used in the authorization code flow to get the token.

func tryAuthCodeFlow(code string) {
    authCodeParams := msalgo.CreateAcquireTokenAuthCodeParameters(config.Scopes, config.RedirectURI, config.CodeChallenge)
    authCodeParams.Code = code
    result, err := publicClientApp.AcquireTokenByAuthCode(authCodeParams)
	if err != nil {
		log.Fatal(err)
    }
    fmt.Println("Access token is " + result.GetAccessToken())
}

//Try silent token acquistion, but this can be skipped
//Assuming the account we want is the first one
var userAccount msalgo.AccountInterfacer
accounts := publicClientApp.GetAccounts()
if len(accounts) == 0 {
    userAccount = nil
} else {
    userAccount = accounts[0]
}
//If the account doesn't exist, try auth code flow
if userAccount == nil {
    tryAuthCodeFlow(authCode)
} else {
    silentParams :=  msalgo.CreateAcquireTokenSilentParametersWithAccount(config.Scopes, userAccount)
    result, err := publicClientApp.AcquireTokenSilent(silentParams)
    //If there is an error with silent acquisition, try auth code flow
    if err != nil {
        log.Info(err)
        tryAuthCodeFlow(authCode)
    } else {
        fmt.Println("Access token is " + result.GetAccessToken())
    }
}

Acquiring token using client secret

//Creating a client credential using a client secret
secret, err := msalgo.CreateClientCredentialFromSecret(config.ClientSecret)
if err != nil {
    log.Fatal(err)
}
//Creating the confidential client app instance
confidentialClientApp, err := msalgo.CreateConfidentialClientApplication(
    clientID, authority, secret)
if err != nil {
    log.Fatal(err)
}
//Try to acquire token from cache
silentParams := msalgo.CreateAcquireTokenSilentParameters(confidentialConfig.Scopes)
result, err := confidentialClientApp.AcquireTokenSilent(silentParams)
//If no token in cache, use the client secret
if err != nil {
    log.Info(err)
    clientSecretParams := msalgo.CreateAcquireTokenClientCredentialParameters(confidentialConfig.Scopes)
	result, err := confidentialClientApp.AcquireTokenByClientCredential(clientSecretParams)
	if err != nil {
		log.Fatal(err)
	}
} 
accessToken := result.GetAccessToken()
log.Info("Access token is: " + accessToken)