Access Microsoft Azure using an OAuth access token
Posted: ,
Modified:
Summary
How to use OAuth access tokens received from Device authorization for Microsoft Azure from Swagger clients generated in Access Microsoft Azure from Go? It is depended on formats of functions generated by Swagger. This post introduces two types of functions and how to attach OAuth tokens to them.
If methods receive authorization information
Some methods receive authorization information as a parameter. For example, in Resource API (2016-09-01) generated by the following command:
$ swagger generate client \
-f https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/arm-resources/resources/2016-09-01/swagger/resources.json \
-t resource
function ResourceGroupsCreateOrUpdate, which creates or updates a resource group, is defines as follows:
func (a *Client) ResourceGroupsCreateOrUpdate(
params *ResourceGroupsCreateOrUpdateParams,
authInfo runtime.ClientAuthInfoWriter
) (*ResourceGroupsCreateOrUpdateOK, *ResourceGroupsCreateOrUpdateCreated, error)
In the above definition, type runtime.ClientAuthInfoWriter is defined in go-openapi/runtime as
import "github.com/go-openapi/strfmt"
type ClientAuthInfoWriter interface {
AuthenticateRequest(ClientRequest, strfmt.Registry) error
}
Package go-openapi/runtime/client has function BearerToken, which generates a ClientAuthInfoWriter from an OAuth token (BearerToken); and we can use it to make authInfo
parameter like
authInfo = httptransport.BearerToken(token)
where token is a string representing the OAuth token.
Note that, there are many client
packages and people usually give an alias httptransport
for package go-openapi/runtime/client.
The following example creates a runtime.ClientAuthInfoWriter and calls function ResourceGroupsCreateOrUpdate with an OAuth token:
import (
// The following packages are generated by Swagger
"github.com/jkawamoto/roadie/cloud/azure/resource/client"
"github.com/jkawamoto/roadie/cloud/azure/resource/client/resource_groups"
"github.com/jkawamoto/roadie/cloud/azure/resource/models"
httptransport "github.com/go-openapi/runtime/client"
"github.com/go-openapi/strfmt"
)
const (
// API version
ResourceAPIVersion = "2016-09-01"
)
// name is a resource group name to be created.
func CreateOrUpdate(ctx context.Context, subscriptionID, name, token string) error{
// Create a Resource API client.
cli := client.NewHTTPClient(strfmt.NewFormats())
// Set a location.
location := "westus2"
// cli has several sub-clients for each API group.
// We now use a client for ResourceGroups API.
created, creating, err := cli.ResourceGroups.ResourceGroupsCreateOrUpdate(
resource_groups.NewResourceGroupsCreateOrUpdateParamsWithContext(ctx).
WithAPIVersion(ResourceAPIVersion).
WithSubscriptionID(subscriptionID).
WithResourceGroupName(name).
WithParameters(⊧.ResourceGroup{
Location: &location,
}), httptransport.BearerToken(token))
if err != nil {
return err
}
// If the resource group has been created, created has non-nil value,
// if the resource group is now being created, creating has non-nil value.
// the rest is omitted.
}
The parameters of ResourceGroupsCreateOrUpdateParams look tricky but they aren’t complicated actually.
If methods don’t receive authorization information
Different from the above method, some methods don’t receive any authoriazation information as parameters. In this case, we need to override transporters and add token information to HTTP requests as a HTTP header.
More precisely, we need to create a runtime.ClientAuthInfoWriter by function BearerToken; and set it to Transport.DefaultAuthentication of an API client made by client.NewHTTPClient(strfmt.NewFormats())
, as we can see in the following code:
// Create an API client
cli := client.NewHTTPClient(strfmt.NewFormats())
// Cast the transporter of the client to *httptransport.Runtime;
// and set ClientAuthInfoWriter.
switch transport := cli.Transport.(type) {
case *httptransport.Runtime:
transport.DefaultAuthentication = httptransport.BearerToken(token)
// Set the transporter to sub clients.
cli.ResourceGroups.SetTransport(transport)
}
In the above code, don’t forget to set the transporter not only to the client made by client.NewHTTPClient but also to sub clients using function SetTransport.
The Resource API’s client is defined as
type ResourceManagementClient struct {
DeploymentOperations *deployment_operations.Client
Deployments *deployments.Client
Providers *providers.Client
ResourceGroups *resource_groups.Client
Resources *resources.Client
Tags *tags.Client
Transport runtime.ClientTransport
}
and has six sub clients. We updated ResourceGroup’s transporter in the above example because we are interested in to call ResourceGroup’s function ResourceGroupsCreateOrUpdateParams. If we want to call other functions, we need to update associated sub clients’ transporters.