Requesting a Token from Azure AppFabric Access Control Service using OAuth 2.0 in C#
Back in September I wrote the article “Requesting a Token from Access Control Service in C#”. In that article I demonstrated the creation of a requesting token, using the Simple Web Token (SWT) token format, and using the OAuth Web Resource Authorization Protocol (WRAP) as the token request protocol. In response ACS will issue a SWT token back which can be used to access web services (commonly REST).
There is one problem though: OAuth WRAP has been deprecated in favor of the OAuth 2.0 specification which the IETF community has been working on over the past year.
The Access Control Service team has been working on supporting the latest revision of OAuth 2.0. As such, it is currently implemented in the Labs CTP version of ACS available at https://portal.appfabriclabs.com/ for testing.
The intent of this article is to provide a simple piece of code to request tokens from ACS using the OAuth 2.0 protocol head instead of OAuth WRAP profile. As such, I will provide a new version of the TokenFactory class I previous posted and I will highlight the differences. For a full understanding of the OAuth 2.0 support in ACS please visit https://acs.codeplex.com/ for documentation and samples.
Also earlier in the year I had written a number of other samples for obtaining tokens from ACS using other languages other than C#, like PHP, Java, and Python. I will not be updating those samples for using OAuth 2.0 instead of WRAP; however, both protocols are relatively simple so it won’t take much effort to figure out how the changes apply for the other languages.
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Net;
using System.Text;
using System.Web.Script.Serialization;
public class TokenFactory
{
private static string acsHost = "accesscontrol.appfabriclabs.com";
string serviceNamespace;
string serviceIdentityName;
string serviceIdentityPassword;
public TokenFactory(string serviceNamespace, string serviceIdentityName, string serviceIdentityPassword)
{
this.serviceNamespace = serviceNamespace;
this.serviceIdentityName = serviceIdentityName;
this.serviceIdentityPassword = serviceIdentityPassword;
}
public string GetACSToken(string relyingPartyApplicationName)
{
string response;
// request a token from ACS
WebClient client = new WebClient();
client.BaseAddress = string.Format(@"https://{0}.{1}/", serviceNamespace, acsHost);
NameValueCollection values = new NameValueCollection();
values.Add("grant_type", "password");
values.Add("client_id", serviceIdentityName);
values.Add("username", serviceIdentityName);
values.Add("client_secret",serviceIdentityPassword);
values.Add("password", serviceIdentityPassword);
try
{
byte[] responseBytes = client.UploadValues("/v2/OAuth2-10/rp/" + relyingPartyApplicationName, values);
string responseData = Encoding.UTF8.GetString(responseBytes);
JavaScriptSerializer serializer = new JavaScriptSerializer();
Dictionary<string, object> decodedDictionary = serializer.DeserializeObject(responseData) as Dictionary<string, object>;
response = decodedDictionary["access_token"] as string;
}
catch (WebException ex)
{
StreamReader reader = new StreamReader(ex.Response.GetResponseStream());
response = reader.ReadToEnd();
}
return response;
}
}
Notice that there are a number of changes from the previous version.
- The acsHost address was changed from “accesscontrol.windows.net” to “accesscontrol.appfabriclabs.com”. The OAuth 2.0 support is only on the Labs CTP release and not yet in the production environment, therefore the address change allows us to work against the proper environment.
- The WRAP protocol required either a SWT or a Username/Password. The previous version used a SWT token which is why there was code to produce the SWT token itself as the token request then it was submitted using WRAP. There is another profile of WRAP which doesn’t require a token and instead just uses the Username/Password. This example doesn’t require a SWT either, it also only depends on the Username/Password which is why there is no code to produce a SWT.
- The latest version of the labs release also includes better error responses, so I added try/catch and the extraction of the error response for my own development-time diagnosis.
- The terminology was updated in this sample to match the terminology that is more familiar in the Management Portal.
- Lastly, there are the obvious changes due to the differences in the protocols. The values being submitted as well as the address needs to be updated. One thing to note is that before the appliesTo property (i.e. the address) was used to identify the Relying Party, now the name of the RP is used instead.
- This code also requires a reference to System.Web and System.Web.Extensions. Previous version only required System.Web, but this version needs the extensions for the JavaScriptSerializer.
Due to these changes Main() also needs to be updated as we no longer need to produce the SWT request and the terminology is updated.
static void Main(string[] args)
{
string serviceNamespace = "...your service namespace...";
string serviceIdentityName = "... your service identity name...";
string serviceIdentityPassword = "...your service identity password...";
string relyingPartyApplicationName = "... your Relying Party application name...";
TokenFactory tf = new TokenFactory(serviceNamespace, serviceIdentityName, serviceIdentityPassword);
string returnToken = tf.GetACSToken(relyingPartyApplicationName);
Console.WriteLine(returnToken);
Console.ReadLine();
}