# Azure Active Directory authentication for Go This is a standalone package for authenticating with Azure Active Directory from other Go libraries and applications, in particular the [Azure SDK for Go](https://github.com/Azure/azure-sdk-for-go). Note: Despite the package's name it is not related to other "ADAL" libraries maintained in the [github.com/AzureAD](https://github.com/AzureAD) org. Issues should be opened in [this repo's](https://github.com/Azure/go-autorest/issues) or [the SDK's](https://github.com/Azure/azure-sdk-for-go/issues) issue trackers. ## Install ```bash go get -u github.com/Azure/go-autorest/autorest/adal ``` ## Usage An Active Directory application is required in order to use this library. An application can be registered in the [Azure Portal](https://portal.azure.com/) by following these [guidelines](https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-integrating-applications) or using the [Azure CLI](https://github.com/Azure/azure-cli). ### Register an Azure AD Application with secret 1. Register a new application with a `secret` credential ``` az ad app create \ --display-name example-app \ --homepage https://example-app/home \ --identifier-uris https://example-app/app \ --password secret ``` 2. Create a service principal using the `Application ID` from previous step ``` az ad sp create --id "Application ID" ``` * Replace `Application ID` with `appId` from step 1. ### Register an Azure AD Application with certificate 1. Create a private key ``` openssl genrsa -out "example-app.key" 2048 ``` 2. Create the certificate ``` openssl req -new -key "example-app.key" -subj "/CN=example-app" -out "example-app.csr" openssl x509 -req -in "example-app.csr" -signkey "example-app.key" -out "example-app.crt" -days 10000 ``` 3. Create the PKCS12 version of the certificate containing also the private key ``` openssl pkcs12 -export -out "example-app.pfx" -inkey "example-app.key" -in "example-app.crt" -passout pass: ``` 4. Register a new application with the certificate content form `example-app.crt` ``` certificateContents="$(tail -n+2 "example-app.crt" | head -n-1)" az ad app create \ --display-name example-app \ --homepage https://example-app/home \ --identifier-uris https://example-app/app \ --key-usage Verify --end-date 2018-01-01 \ --key-value "${certificateContents}" ``` 5. Create a service principal using the `Application ID` from previous step ``` az ad sp create --id "APPLICATION_ID" ``` * Replace `APPLICATION_ID` with `appId` from step 4. ### Grant the necessary permissions Azure relies on a Role-Based Access Control (RBAC) model to manage the access to resources at a fine-grained level. There is a set of [pre-defined roles](https://docs.microsoft.com/en-us/azure/active-directory/role-based-access-built-in-roles) which can be assigned to a service principal of an Azure AD application depending of your needs. ``` az role assignment create --assigner "SERVICE_PRINCIPAL_ID" --role "ROLE_NAME" ``` * Replace the `SERVICE_PRINCIPAL_ID` with the `appId` from previous step. * Replace the `ROLE_NAME` with a role name of your choice. It is also possible to define custom role definitions. ``` az role definition create --role-definition role-definition.json ``` * Check [custom roles](https://docs.microsoft.com/en-us/azure/active-directory/role-based-access-control-custom-roles) for more details regarding the content of `role-definition.json` file. ### Acquire Access Token The common configuration used by all flows: ```Go const activeDirectoryEndpoint = "https://login.microsoftonline.com/" tenantID := "TENANT_ID" oauthConfig, err := adal.NewOAuthConfig(activeDirectoryEndpoint, tenantID) applicationID := "APPLICATION_ID" callback := func(token adal.Token) error { // This is called after the token is acquired } // The resource for which the token is acquired resource := "https://management.core.windows.net/" ``` * Replace the `TENANT_ID` with your tenant ID. * Replace the `APPLICATION_ID` with the value from previous section. #### Client Credentials ```Go applicationSecret := "APPLICATION_SECRET" spt, err := adal.NewServicePrincipalToken( *oauthConfig, appliationID, applicationSecret, resource, callbacks...) if err != nil { return nil, err } // Acquire a new access token err = spt.Refresh() if (err == nil) { token := spt.Token } ``` * Replace the `APPLICATION_SECRET` with the `password` value from previous section. #### Client Certificate ```Go certificatePath := "./example-app.pfx" certData, err := ioutil.ReadFile(certificatePath) if err != nil { return nil, fmt.Errorf("failed to read the certificate file (%s): %v", certificatePath, err) } // Get the certificate and private key from pfx file certificate, rsaPrivateKey, err := decodePkcs12(certData, "") if err != nil { return nil, fmt.Errorf("failed to decode pkcs12 certificate while creating spt: %v", err) } spt, err := adal.NewServicePrincipalTokenFromCertificate( *oauthConfig, applicationID, certificate, rsaPrivateKey, resource, callbacks...) // Acquire a new access token err = spt.Refresh() if (err == nil) { token := spt.Token } ``` * Update the certificate path to point to the example-app.pfx file which was created in previous section. #### Device Code ```Go oauthClient := &http.Client{} // Acquire the device code deviceCode, err := adal.InitiateDeviceAuth( oauthClient, *oauthConfig, applicationID, resource) if err != nil { return nil, fmt.Errorf("Failed to start device auth flow: %s", err) } // Display the authentication message fmt.Println(*deviceCode.Message) // Wait here until the user is authenticated token, err := adal.WaitForUserCompletion(oauthClient, deviceCode) if err != nil { return nil, fmt.Errorf("Failed to finish device auth flow: %s", err) } spt, err := adal.NewServicePrincipalTokenFromManualToken( *oauthConfig, applicationID, resource, *token, callbacks...) if (err == nil) { token := spt.Token } ``` #### Username password authenticate ```Go spt, err := adal.NewServicePrincipalTokenFromUsernamePassword( *oauthConfig, applicationID, username, password, resource, callbacks...) if (err == nil) { token := spt.Token } ``` #### Authorization code authenticate ``` Go spt, err := adal.NewServicePrincipalTokenFromAuthorizationCode( *oauthConfig, applicationID, clientSecret, authorizationCode, redirectURI, resource, callbacks...) err = spt.Refresh() if (err == nil) { token := spt.Token } ``` ### Command Line Tool A command line tool is available in `cmd/adal.go` that can acquire a token for a given resource. It supports all flows mentioned above. ``` adal -h Usage of ./adal: -applicationId string application id -certificatePath string path to pk12/PFC application certificate -mode string authentication mode (device, secret, cert, refresh) (default "device") -resource string resource for which the token is requested -secret string application secret -tenantId string tenant id -tokenCachePath string location of oath token cache (default "/home/cgc/.adal/accessToken.json") ``` Example acquire a token for `https://management.core.windows.net/` using device code flow: ``` adal -mode device \ -applicationId "APPLICATION_ID" \ -tenantId "TENANT_ID" \ -resource https://management.core.windows.net/ ```