Xamarin Forms, Autodesk Forge Access Token

In the previous posts, we went through each platform, looking at how we implement deep linking for each of the platforms. At the end of the deep link flow, we have arrived back within our portable Application object with a URL to process. This URL has a response code at the end that we have to parse out and send back to Forge to get our access and refresh tokens.

This is pretty easy, something like this works:


private string GetResponseCodeFromUrl(string redirectUrl)
{
    string codeParameter = "?code=";
    int pos = redirectUrl.IndexOf(codeParameter);
    string code = redirectUrl.Substring(pos + codeParameter.Length);
    return code;
}

After that we post a REST call to “https://developer.api.autodesk.com/authentication/v1/gettoken”. Inside of the content of the post call, we need to add a string with all of our app keys and codes, in a similar fashion to how we started off the whole authentication process in the first post of the series. The string content looks like this and should be all on one line.

var content = new StringContent($"client_id={ClientId}&client_secret={ClientSecret}&grant_type=authorization_code&code={ResponseCode}&redirect_uri={CallbackUrl}");

See the above link for an explanation of the ClientId, ClientSecret and redirect_uri. The ReponseCode is simply the code that we retrieved using the above GetResponseCodeFromUrl function above.


private AuthData AuthorizationData { get; set; }

public async Task<bool> GetAccessTokenAsync(string redirectUrl)
{
    ResponseCode = GetResponseCodeFromUrl(redirectUrl);
    using (HttpClient client = new HttpClient())
    {
        client.DefaultRequestHeaders.Clear();
        using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "https://developer.api.autodesk.com/authentication/v1/gettoken"))
        {
            request.Content = new StringContent($"client_id={ClientId}&client_secret={ClientSecret}&grant_type=authorization_code&code={ResponseCode}&redirect_uri={CallbackUrl}");
            request.Content.Headers.ContentType = 
                new MediaTypeHeaderValue("application/x-www-form-urlencoded");
            using (HttpResponseMessage response = await client.SendAsync(request))
            {
                string data = await response.Content.ReadAsStringAsync();
                if (response.IsSuccessStatusCode)
                {
                    AuthorizationData = Deserialize<AuthData>(data);
                    return true;
                }
            }
        }
    }

    return false;
}

If all goes well, we end up getting a response with our authorization data. That structure looks like this:

public class AuthData
{
    [JsonProperty("token_type")]
    public string TokenType { get; set; }

    [JsonProperty("expires_in")]
    public int ExpiresInMinutes { get; set; }

    [JsonProperty("refresh_token")]
    public string RefreshToken { get; set; }

    [JsonProperty("access_token")]
    public string AccessToken { get; set; }

    public DateTime DateGranted { get; set; }

    public AuthData()
    {
        DateGranted = DateTime.Now;
    }
}

I am using Json.NET as my deserializer and am using the attributes to redirect the parsed data into the properties that I want. I think pretty much everyone uses Json.NET, but if it is the first you have heard about it, it is definitely worth your time to look at. Important properties above are:

  • AccessToken: use this for all of your Forge requests so that Forge knows you are authenticated.
  • RefreshToken: the access token will expire. You use the refresh token to request a new one without forcing the user to re-authenticate.
  • ExpiresIn: how many minutes before the access token expires.
  • DateGranted: not returned by Forge, but sets when the token was granted so that we can compute when it expires.
  • And that is about it! In the next post, we will look at refreshing the access token.

Xamarin Forms, Autodesk Forge, iOS Deep-Linking

Alright, last time we looked at UWP, and now we will look at iOS. It is a pretty similar effort to UWP. All we need to do is declare the protocol handling and then handle the URL.

Declaring the Protocol

First we have to edit the info.plist file in the iOS project. This file needs to be modified by hand and is just a simple XML file. The plist element is the root element and contains a “dict” object. Each element of the dict object is a setting for the app and it is contained in pairs. For example:

<plist version="1.0">
<dict>
    <key>UIDeviceFamily</key>
    <array>
        <integer>1</integer>
        <integer>2</integer>
    </array>

    ... other settings
</dict>
</plist>

At the end of the file, we can add our protocol declaration. It will look like this:

<plist version="1.0">
<dict>
    ... other settings

    <key>CFBundleURLTypes</key>
    <array>
        <dict>
            <key>CFBundleURLName</key>
            <string>same.asyour.bundle.identifier</string>
            <key>CFBundleURLSchemes</key>
            <array>
                <string>bbsw-fm</string>
            </array>
        </dict>
    </array>
</dict>
</plist>

Handling the URL Link

This is almost exactly like how we did it in the UWP platform project. In the AppDelegate class, we need to override the OpenUrl method and delegate back to the portable Application object.


public partial class AppDelegate : global:Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{

    public override bool OpenUrl(UIApplication app, NSUrl url, NSDictionary options)
    {
        DeepLink.App thisApp = (DeepLink.App) Prism.Unity.PrismApplication.Current;
        thisApp.UwpIOSOnAppLinkRequestReceived(new Uri(url.ToString()));
        return true;
    }
}

All that is happening above is that when the app is activated because of the protocol that it has registered, it will look at the URL and extract the response code that was returned by Forge. It will then use that response code in a REST API call to get the authorization token and refresh token. If that is successful, we perform a navigation to the next page in our app. Obviously the code isn’t complete as it doesn’t handle any kinds of errors. For the details on how to get the authorization and refresh tokens see the previous post.

Next up and finally will be the Android implementation.

Xamarin Forms and Forge Deep Linking

So things have been a bit quiet lately, I have been pretty busy at work and actually ended up a bit under the weather for a while, but am recovering now and want to continue on with this series of posts.

Last time I wrote about authenticating on Xamarin Form to Autodesk Forge using a web view to handle the authorization events. The use of webviews is being discouraged in some areas. It has been hard to get to the reasoning behind this, but some people are saying that it is possible for an attacker to fake in their own malicious site to replace the authentication page. I’m not sure how much of a worry this is, especially in mobile where both the app and the webview are sandboxed. Instead Google is recommending that you use the system browser directly. If nothing else, it will at least allow your user to use single-sign-on.

So how do we do this? First of all you have to modify your Autodesk Forge app to specify a different callback URL, one with your custom protocol. In my case, I used bbsw-fm://brainbucketsoftware.com.

Now when you start your authentication, instead of telling the webview to navigate you use the Device.Url command such as below (formatted for the blog, but obviously all one line).


string BaseUrl = "https://developer.api.autodesk.com/authentication/v1/authorize";
private const string ResponseType = "code";
private const string ClientId = "xxxxxxxxxxxxxxxxxxxxxxx";
private const string ClientSecret = "yyyyyyyyyyyy";
private const string Scope = "data:write%20data:read%20data:create";
private const string CallbackUrl = "bbsw-fm://brainbucketsoftware.com/";
private const string CodeParameter = "?code=";

string url = $"{BaseUrl}?response_type={ResponseType}&client_id={ClientId}&redirect_uri={CallbackUrl}&scope={Scope}";
Device.OpenUri(new Uri(url));

The next thing we need to do is override the OnAppLinkRequestReceived method in the App.xaml in your shared/portable project.


protected override async void OnAppLinkRequestReceived(Uri uri)
{
    base.OnAppLinkRequestReceived(uri);
    // same code as was used in the webview sample
    bool retrievedToken = await GetAccessTokenAsync(uri.ToString());
    if (retrievedToken)
        await NavigationService.NavigateAsync(Pages.Test);
}

The above function is supposed to be called automatically by the Xamarin Forms objects when your registered protocol is invoked on the device. However, I found that it only worked on Android and wasn’t being called on iOS or UWP (I believe there was a bug filed for this with Xamarin). So I created another entry point in my App object for those platforms which I ended up calling manually in the platform specific code.


public void UwpIOSOnAppLinkRequestReceived(Uri uri)
{
    OnAppLinkRequestReceived(uri);
}

Next post we will take a look at what we need to do for UWP platform code.