After more than one year, three developer previews and a ton of feedback from customers and partners (that would be you! Thank you!!!) today we are finally announcing the general availability of the Active Directory Authentication Library (ADAL) for .NET v1.0!
You can download it directly from the NuGet gallery or get it from Visual Studio.
Through the year we produced a lot of material on ADAL, but between name changes (it started its existence as Windows Azure Authentication Library – AAL) and features set variations it might not be super easy for you to get a good idea of what the product does. The good news is that the MSDN documentation for ADAL is on its way. Also, I am going to take this chance to pretend that I never wrote anything about ADAL and use this post to (re)introduce the library to you, so that you can be confident that what you are reading is up to date for the RTM version. I will also try to convey the why’s behind the current design, which are likely to make this a pretty long post. So, grab your favorite caffeine delivery vessel and read on!
What Is the Active Directory Authentication Library (ADAL)?
If you are into definitions, here’s one for you:
The Windows Azure Authentication Library (ADAL) is a library meant to help developers to take advantage of Active Directory for enabling client apps to access protected resources.
In more concrete terms. If you have a resource (Web API or otherwise) that is secured via Active Directory, and you have a client application that needs to consume it, ADAL will help you to obtain the security token(s) the client needs to access the resource. In addition, ADAL will help you to maintain and reuse the tokens already obtained.
That’s pretty much it, actually. The rest is all about qualifying a bit better the terms I used:
Active Directory – I am using the term in its broadest sense. In practice: ADAL can work with Windows Azure AD, Windows Server AD (it requires the ADFS version in Windows Server 2012 R2) and ACS namespace.
Protected resource – In theory this can be pretty much anything that can be remotely accessed; in practice, we expect it to be a REST service in the vast majority of cases.
”Protected” in this context means that the service expects the caller to present an access token, which must be verified as coming from the right AD before granting access.
Client app – This probably calls to mind the classic rich client applications, something with a UI built on the native visual elements of the platform it targets. ADAL can totally work with those, I’d even daresay they are the top scenario for this release, but they are not the only one: any app operating in a client role is a candidate. In practice, any application that is not a browser (code-behind of web sites, long running processes, workers, batches, etc) and needs to request a token to access a resource is a client app that can take advantage of ADAL.
Today’s release, the v1 of ADAL .NET, applies those principles to the .NET platform. However all of the above holds for ADAL in general: different platforms will have different capabilities and admit different app types, but that’s the general scope.
Now that you know what ADAL is for, let’s focus on how it operates.
Whereas in the past our libraries surfaced all the constructs and concepts of the protocols and artifacts used in the mechanics of authentication, in ADAL we decided to focus on the scenario and the high level tasks. That allows us to eliminate, or at the very least late-bind, a lot of the complexity that with the traditional approaches would be something inescapable even for the simplest uses. Skeptical? A healthy attitude, very good. Go on, all will be revealed!
ADAL’s Main Pattern
Identity is hard. There are so many things one needs to keep track of! Token formats, what protocol to use for a given topology, which parameters work for one identity provider but not for the other, how to prevent the user from being prompted every time, how to avoid saving passwords and secrets, what to do when you need multiple authentication factors, and many more obscure details.
…And yet, a high level description of how things work sounds so simple:
- I have a client app
- I want to call a service, but it requires me to present a token
- I go to some kind of authority, and I do whatever is necessary to get a token for the resource
- once I have the token, I call the resource
Not hard at all, right? By virtue of its lack of details, the above describes pretty much all known client-calls-an-API scenarios: a WPF app calling a local service, a console app accessing a Web API running on Windows Azure, a web site code-behind calling the Bing Maps API, a continuous build integration worker process extracting stuff from a queue service, a web site using OAuth2 obtaining and using a token for delegated access to an API, and so on.
What if we could work at that level, instead of preoccupying ourselves with all sorts of details? Well, what you see diagrammed above is pretty much the ADAL object model. I mean it fairly literally.
ADAL’s main class, AuthenticationContext, represents in your app’s code the authority (Windows Azure AD tenant, Windows Server ADFS instance or ACS namespace) from where you want to get tokens from.
AuthenticationContext’s main method, AcquireToken, is the primitive which allows you to perform leg #1 in the diagram. The parameters will vary depending on the concrete scenario you are dealing with (more about that later) but in general you can expect to have to qualify which app is requesting the token and for which resource. Both are passed using the identifiers with which the client app and the resource are known by the authority (all actors must have been provisioned, AD will not issue tokens for unknown entities).
Upon successful authentication, AcquireToken returns an AuthenticationResult, which contains (among other things) an access token for the target service.
Once you have a token, the ball is on your court. ADAL does not force you to use channels or special proxies, nor it imposes you to use any specific protocol for accessing your target resource. You are responsible for plugging the token in the request to your resource, according to the protocol(s) it supports. In the vast majority of cases we observed during the preview that’s OAuth2.0, which just means that you have to add it in the right HTTP header of the request, but we did have somebody injecting the token in a WCF channel. It’s entirely up to you.
One last thing I’d highlight at this point is that every time you get a token from the authority ADAL adds it to a local cache. Every subsequent call to AcquireToken will examine the cache, and if a suitable token is present it will be returned right away. If a suitable token cannot be found, but there is enough information for obtaining a new one without repeating the entire authentication process (as it is the case with OAuth2 refresh tokens) ADAL will do so automatically. The cache is fully queryable and can be disabled or substituted with your own implementation, but if you don’t need either you don’t even need to know it’s there: AccessToken will use it transparently.
There you have it: now you understand what ADAL does, even how to do use it almost at the code level. Remarkably, all this didn’t require forcing you to grok any security protocol concept aside from a nondescript “token”. If you are still skeptical you might ask “where’s the trick? Where did all the complexity go?”. There is no trick, of course; but there are tradeoffs which are intrinsic to the problem, and I want to be transparent about those to set the right expectations. So, for the question “where did the complexity go” here there are few answers.
- It was moved from the code to the AD setup. AD, in all the flavors supported by ADAL, has the ability of maintaining descriptions of entities (client apps, web APIs, users) and the relationships which tie them together (“can client A call service B?”). ADAL fully relies on that intelligence, passing around identifiers that are actually references to what AD already knows about apps. That makes the code MUCH simpler, but in turn requires every entity you want to work with to be registered in your authority of choice. If in your company somebody else (like and administrator) does that for you, that’s a net gain for you the developer as you can leverage their work and avoid reinventing the wheel. If you are a one-man band an you wear both the hat of the developer and of the administrator, you are responsible for doing all the necessary provisioning before being able to code against the scenario. It’s still a big advantage (do it once, use it from multiple apps; maintain neater code; etc) but somebody got to take care of that.
- It was pruned by the pre-selection of fixed scenarios. ADAL gets tokens only from AD. To support that, it implements a number of protocols and artifacts: but those are not exposed to you for general use. ADAL’s API surface is tied to the AD-based topologies supported at this point in time, and its object model reflects that. That allows us to maintain a super simple set of primitives, but it also means that there’s little margin for customization or use beyond the intended scope.
- Dealing with finer details is delayed to the last possible moment. The high level description of the token based authentication flow is accurate for all scenarios, and an OM based on it is easy to understand for everybody. However, once the rubber hits the road it is inevitable that you’ll have to deal with some concrete aspects that are specific to the scenario you are implementing. For example: if you are writing a native client app and you want to get a token for a specific user, you’ll have to let AcquireToken know about it; conversely, if you are developing a long running process there might be no user involvement at all, but you might have to pass to AcquireToken an X.509 certificate to identify your app with AD.
How does ADAL reconcile those two abstraction levels? Simple. The act of acquiring a token is done always through the same primitive, AcquireToken. However such primitive provides multiple overloads, specialized for the scenarios ADAL supports.
The advantage of this approach is that if you write a native client app you only need to learn about the overloads that work for you, and you can ignore the ones for worker processes. Of course the issue becomes finding the right overload for your scenario! However we count on the fact that you know what are the entities that are used in your scenario, hence zeroing on the overload that has the right placeholders should be pretty straightforward.
Bottom line: those scenarios have intrinsic complexity, but we did our best to factor things so that it is minimized and does not spill across scenarios.
What Scenarios/Topologies ADAL .NET v1 Supports?
Let’s get more concrete and take a look at the typical scenarios/topologies you can implement with ADAL.
Important: although ADAL’s object model abstracts away most differences between authority types (Windows Azure AD, Windows Server AD /ADFS, ACS) such differences exist: that means that certain scenarios will be supported when the authority is Windows Azure AD but not when it is an ACS namespace, and similar. I’ll call it out in the descriptions.
Native Client – Interactive User Authentication
Windows Azure AD/ADFS
This is the case in which you are developing a native app (anything that is not a browser and that can show UI, that includes consoles) and you want to access a resource with a token obtained as the current interactive user. This is probably ADAL’s easiest flow.
Peeking under the cover, for Windows Azure AD and for ADFS this flow is implemented using the OAuth2 code grant for public clients. Code-wise, you write something like the following:
AuthenticationContext _authenticationContext =
new AuthenticationContext("https://login.windows.net/mytenant.onmicrosoft.com"); AuthenticationResult _authenticationResult =
The first line creates an AuthenticationContext tied to the AAD tenant “mytenant”. I would use the exact same logic for an ADFS based authority; the visible difference would be that the AuthenticationContext would be initialized with the address of the ADFS instances’s root endpoints (typically hostname+”/Adfs/”).
The second line asks to mytenant to issue a token for the service with identifier http://myservices/service1, communicates that the request comes from client with ID a8cb2a71-da38-4cf4-9023-7799d00e09f6 and passes in the return URI (used as a terminator in the flow against the authorization endpoint: an OAuth2 protocol detail).
If this is the first time we execute that code, the effect of the call to AcquireToken will be to pop out a browser dialog gathering the user credentials:
ADAL handles creation of the dialog, crafting of the initial URL, navigation and every other aspect in full transparency. The dialog is really just a browser surface, and the authority decides what to send in term of experience: here we got username & password, but specific users might get multiple auth factors, consent prompts and similar.
Upon successful authentication, the AccessToken will be in _authenticationResult. The content _authenticationResult gets cached: subsequent calls to AcquireToken using the same parameters will yield the results from the cache until an expiration occurs.
You can find a detailed sample showing this scenario in this code gallery entry.
_authenticationContext = new AuthenticationContext("https://mynamespace.accesscontrol.windows.net"); List<IdentityProviderDescriptor> idps = (List<IdentityProviderDescriptor>) _authenticationContext.GetProviders("http://myservices/service1"); AuthenticationResult result1 = _authenticationContext.AcquireToken("http://myservices/service1", idps);
The first line creates an AuthenticationContext instance tied to our ACS namespace.
The 2nd line reaches out for ACS’ info feed and extracts all of the IdPs supported by the target RP.
The 3rd line asks for a token for our RP (the resource) using the first IdP from the list. This will pop out a browser, already pointed to the IdP of choice, and use it to drive the authentication process. As for the AD case, the results will be cached.
An example can be found here.
Server to Server – Client Credentials Grant
This is the scenario in which the client application itself has its own credentials, which are exchanged for an issued token for the specified resource. Under the hood this is implemented as a OAuth2 client credentials grant, though different authority types use different OAuth2 drafts; in any case that’s fully immaterial to you given that the library takes care of picking the right style in full transparence. This topology is not available for ADFS. Also, different authority types support different crednetial types. Check the MSDN documentation for details.
_authenticationContext = new AuthenticationContext("https://login.windows.net/21211b42-1e25-4046-8872-d8832a38099b"); ClientCredential clientCred2 = new ClientCredential("2188a797-7d21-41dc-84f3-3c1720262614", "HBheXXXXXXXXXXXXXXXXXXXXXXXXXXXM="); AuthenticationResult _authenticationResult = _authenticationContext.AcquireToken("https://localhost:9001", clientCred2);
The flow here introduces a new concept, the credential, which needs to be initialized for the client and passed to AcquireToken. There are other credential types, such as X.509, but the differences are only syntactic.
Confidential Client Code Grant
This scenario is the most classic OAuth2 flow. ADAL .NET expects you to obtain the code on your own, given that the exact flow would depend on the stack you are developing against (web forms?MVC?).
Once you have that, you can pass it to AcquireTokenByAuthorizationCode. The method name departs from the main AcquireToken because it does not fully participate in the ADAL’s mainstream flow (for example, it does not save results in the cache).
We don’t have a sample for this, but we should publish one soon.
The above are the main supported topologies. ADAL also have some cross-cutting features that can improve your control over a specific scenario or make it easy to target specific types opf applications (e.g. apps that deal with a single user for their entire lifetime, vs apps which maintain multiple users at once).
Here I am going to list the main ones in no particular order, and give you a super-quick hit at what they are useful for; each of them is worth its own blog post, and I’ll try to diligently make that happen in the weeks ahead.
Resource Driven Discovery
Resources receiving unauthenticated or incorrectly secured requests can send back a challenge that indicates useful info such as which authority they trust, what is their resourceID, and so on. ADAL is capable of reading the format of that challenge and trigger a token acquisition on the basis of that.
Instance discovery and validation
ADAL protects you from resources forwarding you to malicious authorities by validating the authority URL against known templates. Note, this holds for AAD but ADFS is not currently capable of automated validation hence for ADFS scenarios you need to opt out of this function (at AuthenticationContext construction time).
Cache related features
ADAL comes with a default in-memory cache which spans the process, and that gets used automatically. That cache is fully queryable via LINQ, and contains far more than just tokens: when available, the cache will also contain user info such as identifiers, first and last name, and so on.
You can easily implement your own cache and plug it in: you might want to do so when you want a persistent store, enforce your own boundaries between cache stores associated to different AuthenticatonContext instances, and so on.
Broad Use Refresh Tokens
AAD issues refresh tokens that can redeemed for access tokens associated to any resource in the tenant, as opposed to just the resource they were originally obtained for. ADAL is aware of this possibility, and will actively take advantage of it when available.
AAD offers a “common” endpoint not tied to any specific tenant; when used, such endpoints allows the end user to determine which tenant should be used according to the UPN he or she enters in the username field. ADAL supports late-binding AuthenticationContext which start against common but are adjusted after the first authentication disambiguates the tenant.
ADAL offers various flags for exercising more control over how the experience takes place, such as flags for guaranteeing that the end user will be prompted no matter what’ in the cache
Direct use of refresh tokens
For expert users, ADAL offers methods for using refresh tokens directly. Those methods do not affect the cache content.
To Learn More
ADAL is designed to help you to take advantage of Active Directory in your apps with a simple object model which does not require you to get a PhD in protocols & information security. It is one of the most obvious examples of the on-premises/cloud symmetry in Microsoft’s offering, given that the same code can be pointed to a Windows Azure AD or to an ADFS with practically no changes. I am incredibly proud of what the team has accomplished, and I feel privileged to have had the chance of working with such fine engineers on this key component.
Now that we finally hit GA, you can use ADAL .NET in production in your own apps: if you have questions, doubts or feedback feel free to hit the Contact tab and send me a not!
Also: ADAL .NET is just the first release of the ADAL family. Stay tuned for news in this area
Vittorio Bertocci is a developer, speaker, published author, avid reader, troublemaker, foodie, Italian expat, and other things that would not be wise to mention here. This is Vittorio's personal blog. The views and opinions expressed here are his, and not those of his employer.Follow @vibronet
@WadeWegner ha. This confirms my bias against optical tracking. Can't say I wasn't vocal about it :)
@flytzen far too kind, thank you! Please let me know your thoughts once you get through it :)
"that's Wally is too." dilbert.com/strip/2016-02-…
- January 2016 (2)
- December 2015 (2)
- September 2015 (2)
- August 2015 (4)
- July 2015 (1)
- June 2015 (1)
- May 2015 (2)
- April 2015 (1)
- March 2015 (3)
- February 2015 (4)
- January 2015 (1)
- November 2014 (4)
- October 2014 (5)
- September 2014 (4)
- August 2014 (2)
- July 2014 (8)
- June 2014 (1)
- May 2014 (4)
- April 2014 (4)
- March 2014 (6)
- February 2014 (5)
- January 2014 (1)
- December 2013 (4)
- November 2013 (2)
- October 2013 (9)
- September 2013 (3)
- August 2013 (3)
- July 2013 (5)
- June 2013 (8)
- May 2013 (4)
- April 2013 (17)
- March 2013 (16)
- February 2013 (6)
- January 2013 (4)
- December 2012 (3)
- November 2012 (5)
- October 2012 (1)
- August 2012 (3)
- July 2012 (6)
- June 2012 (9)
- April 2012 (1)
- March 2012 (7)
- February 2012 (1)
- December 2011 (1)
- November 2011 (1)
- October 2011 (2)
- September 2011 (1)
- August 2011 (2)
- July 2011 (5)
- June 2011 (2)
- May 2011 (15)
- April 2011 (9)
- March 2011 (2)
- February 2011 (5)
- January 2011 (8)
- December 2010 (3)
- November 2010 (3)
- October 2010 (6)
- September 2010 (6)
- August 2010 (8)
- July 2010 (2)
- June 2010 (9)
- May 2010 (13)
- April 2010 (4)
- March 2010 (6)
- February 2010 (1)
- December 2009 (3)
- November 2009 (16)
- September 2009 (3)
- August 2009 (5)
- July 2009 (6)
- June 2009 (7)
- May 2009 (10)
- April 2009 (11)
- March 2009 (4)
- February 2009 (2)
- January 2009 (6)
- December 2008 (3)
- November 2008 (9)
- October 2008 (3)
- September 2008 (5)
- August 2008 (7)
- July 2008 (8)
- June 2008 (6)
- May 2008 (6)
- April 2008 (11)
- March 2008 (10)
- February 2008 (9)
- January 2008 (12)
- December 2007 (6)
- November 2007 (5)
- October 2007 (10)
- September 2007 (4)
- August 2007 (1)
- July 2007 (1)
- June 2007 (17)
- May 2007 (8)
- April 2007 (10)
- March 2007 (10)
- February 2007 (4)
- January 2007 (6)
- December 2006 (2)
- November 2006 (3)
- October 2006 (5)
- September 2006 (3)
- August 2006 (7)
- July 2006 (3)
- June 2006 (7)
- May 2006 (4)
- April 2006 (6)
- March 2006 (8)
- February 2006 (3)
- January 2006 (2)
- December 2005 (5)
- November 2005 (2)
- October 2005 (6)
- July 2005 (12)
- June 2005 (6)
- May 2005 (3)
- April 2005 (8)
- March 2005 (4)
- February 2005 (14)
- January 2005 (9)
- December 2004 (5)
- November 2004 (1)
- October 2004 (3)
- June 2004 (3)
- May 2004 (3)
- April 2004 (3)
- March 2004 (1)
- February 2004 (2)
- January 2004 (3)
- December 2003 (5)
- November 2003 (5)
- October 2003 (5)
- September 2003 (5)
- July 2003 (2)
- June 2003 (4)
- May 2003 (1)
- April 2003 (9)