In a previous post we discussed about the three ways to setup Windows 10 devices for work with Azure AD. I later covered in detail how Windows 10 domain joined devices are registered in Azure AD. In this post I want to provide some insight about what happens behind the scenes when users join devices to Azure AD (Azure AD Join).
Users can join devices to Azure AD in two ways: 1) through the out-of-box experience (OOBE) the very first time a device is configured (or after a device reset to factory settings) or 2) through Settings after configuring the device with a Microsoft account (e.g. Hotmail) or local account.
In both cases what happens behind the scenes is fundamentally the same:
- User chooses to join device to Azure AD.
- User authenticates and provides an MFA proof (if configured).
- User accepts terms from MDM system (if applicable).
- Device registers with Azure AD.
- Device enrolls into MDM system and gets sign-in policy (if applicable).
- User signs into Windows.
- User provisions Microsoft Passport for Work.
- Device encryption is enabled and BitLocker key is escrowed to Azure AD.
- User enterprise settings are applied.
Let’s take a look at the details of what happens at each phase.
(1) User chooses to join device to Azure AD
When a user turns a device for the first time the user will see the OOBE. Once the user has gone through the initial pages like choosing language/region, accepting legal terms and connecting to the WiFi, the user sees the experience that allows the user to configure the device with a particular account.
The first detail to know is that this experience is web driven and runs under a particular “temporal” user that is created just before the experience shows. This differs from Windows 7/8.1 where all pages are local and run as SYSTEM up to the point of user logon.

Web pages are rendered in a special host called the Cloud eXperience Host (CXH) which has access to particular WinRT APIs needed for setting up the device.
Once the CXH is launched it navigates to a web app that will orchestrate the setup process. If the device runs Windows Professional the user will see a page presenting the option to configure the device as work-owned (using a work account) or personal (using a Microsoft account). If the device runs Windows Enterprise or Windows Home this page won’t show but instead will default to work-owned or personal respectively.

Once the user has chosen to configure the device as work-owned, the user will have the option to join the device to Azure AD or to create a local account. The current experience shows the option to join the device to a domain (traditional Domain Join) however this option will guide the user to setup a local account for the user to run Domain Join via Settings afterwards.

Please note that we are working on improving this experience on a future update of Windows based on feedback we have received from you.
If the user runs Azure AD Join from Settings (after setting up the device as personal or with a local account) the user will see the experience described from this point on.
After choosing Azure AD Join the CXH will navigate to the Azure AD Join web app that is hosted in the following location:
https://login.microsoftonline.com/WebApp/CloudDomainJoin/4
This web app is mainly “client” code in the form of HTML and JavaScript that calls particular WinRT APIs in the system via the CXH.
(2) User authenticates and provides an MFA proof if configured
Now, the web app will reach out to Azure AD to discover auth end-points by retrieving the OpenID configuration:
GET https://login.microsoftonline.com/common/.well-known/openid-configuration
Configuration is retrieved by obtaining the following JSON document:
{
"authorization_endpoint":"https://login.microsoftonline.com/common/oauth2/authorize",
"token_endpoint":"https://login.microsoftonline.com/common/oauth2/token",
"token_endpoint_auth_methods_supported":["client_secret_post","private_key_jwt"],
"jwks_uri":"https://login.microsoftonline.com/common/discovery/keys",
"response_modes_supported":["query","fragment","form_post"],
"subject_types_supported":["pairwise"],
"id_token_signing_alg_values_supported":["RS256"],
"http_logout_supported":true,
"response_types_supported":["code","id_token","code id_token","token id_token","token"],
"scopes_supported":["openid"],
"issuer":"https://sts.windows.net/{tenantid}/",
"claims_supported":["sub","iss","aud","exp","iat","auth_time","acr","amr","nonce","email","given_name","family_name","nickname"],
"microsoft_multi_refresh_token":true,
"check_session_iframe":"https://login.microsoftonline.com/common/oauth2/checksession",
"end_session_endpoint":"https://login.microsoftonline.com/common/oauth2/logout",
"userinfo_endpoint":"https://login.microsoftonline.com/common/openid/userinfo"
}
The web app will build a sign-in request using the discovered authorization endpoint to obtain a token to Azure DRS:
GET https://login.microsoftonline.com/common/oauth2/authorize
?client_id=01cb2876-7ebd-4aa4-9cc9-d28bd4d359a9
&msafed=0
&nonce=5724294428562708
&prompt=login
&redirect_uri=https%3A%2F%2Flogin.microsoftonline.com%2FWebApp%2FCloudDomainJoin%2F4
&response_type=id_token
&scope=openid+sid
&uxoptin=true
&windows_api_version=2.0
A few things to note:
- The value of the parameter client_id corresponds to the one of Azure DRS. This is interesting because the redirect URI is not the Azure DRS end-point but the Azure AD Join web app. In this special case the Azure AD Join web app is considered a client of Azure DRS.
- The token requested is an ID token. This is because the Azure AD Join web app needs to get claims from the token that need to pass to APIs for discovery, registration and MDM enrollment. Remember that the Azure AD Join web app is considered a client of Azure DRS.
- There is a parameter particular to Windows to specify the API version. The value of 2.0 allows obtaining MDM related URLs (for later use) as claims in the ID token.
The user will see the sign-in page where to enter credentials:

Once the user types the username the page will discover the corresponding realm information. This will determine whether the user needs to be redirected to a different STS (Secure Token Service) like an AD FS on-premises. This is done by retrieving realm information about the user’s domain name:
GET https://login.microsoftonline.com/common/userrealm/
?user=jairoc@microsoft.com
&api-version=2.1
The following is an example of a JSON object for the realm information for a federated tenant:
{
"IsDomainVerified":0,
"NameSpaceType":"Federated",
"federation_protocol":"WSTrust",
"Login":"jairoc@microsoft.com",
"AuthURL":"https://msft.sts.microsoft.com/adfs/ls/?username=jairoc%40microsoft.com&wa=wsignin1.0&wtrealm=urn%3afederation%3aMicrosoftOnline&wctx=",
"DomainName":"microsoft.com",
"FederationBrandName":"Microsoft"
}
If the tenant is federated the user will see the on-premises STS sign-in page where user can enter credentials . If the tenant is managed the user will be able to enter the password directly in the same page. In either case, if MFA has been configured the user will be challenged for additional factors of auth before proceeding.

For managed tenants only, an authentication buffer is created locally and temporary cached for automatic sign-in of the user to Windows at the end of OOBE (this doesn’t happen if run from Settings which will require a sign-out and a manual sign-in by the user). Federated tenant users will need to authenticate to the Windows logon UI after OOBE has completed.
Credentials are posted to Azure AD for authentication to the login end-point along with a few parameters indicating that this is a CXH driven authentication. This is useful to tweak some behaviors on the service side:
https://login.microsoftonline.com/common/login
?cxhflow=MOSET
&cxhver=1.0
&cxhplatform=Desktop
Note that it indicates where the join is run from (Settings or OOBE), the version of the host (to accommodate for future behaviors) and the platform (Desktop or Mobile).
After authentication succeeds, an ID token is generated and posted back to the Azure AD Join web app. The following is an example of the contents of the token:
{
upn : "jairoc@microsoft.com"
instance : "NA"
amr : ["pwd","mfa"]
tid : "df5be8b1-f322-456c-acff-b13066d72402"
sid : "S-1-12-1-1875800440-1278975369-1393086615-305892544"
nbf : 1453828246
ver : "1.0"
mdm_enrollment_url : "https://manage-beta.microsoft.com/EnrollmentServer/Discovery.svc"
family_name : "Cadena"
sub : "4ySzMn-D18vwlmB4lWNipSg7Y5-qDVg9g41XVyoRvF4"
user_setting_sync_url : "Discovery:https://kailani.one.microsoft.com"
onprem_sid : "S-1-5-21-1515794938-1370939529-2107639213-1112"
oid : "6fce7178-9d89-4c3b-97d0-0853c08c3b12"
exp : 1453832146
given_name : "Jairo"
iat : 1453828246
iss : "https://sts.windows.net/df5be8b1-f322-456c-acff-b13066d72402/"
aud : "dd762716-544d-4aeb-a526-687b73838a22"
name : "Jairo Cadena"
unique_name : "jairoc@microsoft.com"
tenant_display_name : "Microsoft"
primary_sid : "S-1-5-21-1515794938-1370939529-2107639213-1112"
}
Please note that this information is cached locally in the device and is accessible after device registration completes through the following APIs in dsreg.dll:
DsrGetJoinInfo
or
DsrGetJoinInfoEx
The following claims in particular contain URLs which are used later on to complete configuration of the device.
mdm_tou_url
mdm_enrollment_url
user_setting_sync_url
To know how, please keep reading :).
(3) User accepts terms from MDM (if applicable)
The next step is for the user to accept the terms from the MDM. If there is a corresponding URL configured in Azure AD for the MDM app for this user, the ID token will contain a claim mdm_tou_url. For more information about how these URLs are setup see the post in the Active Directory blog about Windows 10 Azure AD and Microsoft Intune MDM enrollment.
If present the web app will navigate to the URL and the user will be presented with the terms of acceptance.
Upon acceptance the page posts back an acceptance buffer that the web app will use later when calling the MDM enrollment API.
(4) Device registers with Azure AD
Via JavaScript the web app calls a WinRT worker API that in turn calls APIs for discovery of the registration service, device registration and MDM enrollment:
CloudDomainJoin.DataModel.CloudDomainJoinWorker
This API first calls a “discovery” API to obtain information about the registration service (i.e. Azure DRS). This API is an internal Win32 API implemented in dsreg.dll which retrieves the discovery data document from:
https://enterpriseregistration.windows.net/microsoft.com/enrollmentserver/contract?api-version=1.2
This document contains information about Azure DRS registration and authentication end-points including the federation STS end-point (if configuration is federated), the Azure DRS URI, the end-point for Microsoft Passport provisioning, etc. One example of this document is shown below:
https://enterpriseregistration.windows.net/EnrollmentServer/DeviceEnrollmentWebService.svc
urn:ms-drs:enterpriseregistration.windows.net1.0
https://login.microsoftonline.com/microsoft.com/oauth2/authorize
https://login.microsoftonline.com/microsoft.com/oauth2/token
https://msft.sts.microsoft.com/adfs/ls/
https://enterpriseregistration.windows.net/EnrollmentServer/device/
urn:ms-drs:enterpriseregistration.windows.net1.0
https://msft.sts.microsoft.com/
https://device.login.microsoftonline.com/
https://enterpriseregistration.windows.net/
https://enterpriseregistration.windows.net/EnrollmentServer/key/
urn:ms-drs:enterpriseregistration.windows.net1.0
Please note that some of these URLs/URIs are added to the cache data accessible by the DsrGetJoinInfoEx API after device registration completes (not the DsrGetJoinInfo).
With the discovery data and the ID token obtained during authentication the worker API then calls another API to perform device registration. This is another internal Win32 API in dsreg.dll which does the following:
- Generates a key pair for the device certificate.
- Generates a certificate signing request (CSR) using the key pair above (signs CSR data with private key plus includes public key in request).
- Generates a second key pair that will be used to bind SSO tokens physically to the device when authenticating to Azure AD later on. This key is typically called the storage/transport key (Kstk) and is derived from the SRK (Storage Root Key) of the device TPM. The way the binding of the SSO token to the device is achieved is by storing into the TPM a corresponding symmetric session key (encrypted to the storage/transport key) issued along with the SSO token upon auth to Azure AD. Along with the key pair, obtains attestation data in the form of a blob. For more details about how this please look for a couple of future posts where I will talk about SSO in Windows 10 devices and Internals of Microsoft Passport for Work.
- Sends a device registration request to Azure DRS (to end-point obtained from discovery document) passing along the ID token, the generated CSR and the public portion of the storage/transport key along with its attestation data.
Once the request comes to Azure DRS, the service will validate the token, will create a corresponding device object in Azure AD and will generate and send back a certificate to the device. The API in turn will install the certificate into the LocalMachine\MY store.
Using Windows PowerShell you can easily spot this certificate checking for the CN=MS-Organization-Access particular issuer:
PS C:\> dir Cert:\LocalMachine\My\ | where { $_.Issuer -match "CN=MS-Organization-Access" } | fl
Subject : CN=fbfcc7db-3b31-4818-aaf0-15aeb1ab1875
Issuer : DC=net + DC=windows + CN=MS-Organization-Access + OU=82dbaca4-3e81-46ca-9c73-0950c1eaca97
Thumbprint : A948413B32FA0A6435275693D4136BE10DFB4E97
FriendlyName :
NotBefore : 1/30/2016 10:27:39 AM
NotAfter : 1/27/2026 10:57:39 AM
Extensions : {System.Security.Cryptography.Oid, System.Security.Cryptography.Oid, System.Security.Cryptography.Oid,
System.Security.Cryptography.Oid...}
Also using MSOL Devices cmdlets you can query the device object in Azure AD:
PS C:\> Get-MsolDevice -DeviceId fbfcc7db-3b31-4818-aaf0-15aeb1ab1875
Enabled : True
ObjectId : 3772980f-8553-402f-b092-7bd35395a6bd
DeviceId : fbfcc7db-3b31-4818-aaf0-15aeb1ab1875
DisplayName : DESKTOP-51PBLPR
DeviceObjectVersion : 2
DeviceOsType : Windows
DeviceOsVersion : Windows 10
DeviceTrustType : Azure AD Joined
DeviceTrustLevel : Compliant
DevicePhysicalIds : {}
ApproximateLastLogonTimestamp : 1/30/2016 6:57:39 PM
AlternativeSecurityIds : {X509:<SHA1-TP-PUBKEY>A948413B32FA0A6435275693D4136BE10DFB4E97aOQeN/3X9Ak0C6mIWQSOeM17t
wLhlA2UXOYTHLJutFw=}
DirSyncEnabled :
LastDirSyncTime :
RegisteredOwners : {jairoc@ntdev.microsoft.com}
GraphDeviceObject : Microsoft.Azure.ActiveDirectory.GraphClient.Device
A few things to highlight:
- DeviceId is a GUID generated by Azure DRS. Note that this is the same value in the Subject field of the certificate.
- AlternativeSecurityIds contains the certificate thumbprint with a specific scheme format (i.e. “X509:<SHA1-TP-PUBKEY>:” + thumbprint). This is how Azure AD will find the device object when the device presents the certificate upon authentication.
- The storage/transport key is stored in the device object in an attribute non-visible through Graph API.
(5) Device enrolls into MDM system and gets policy
Upon successful registration the worker API calls the MDM enrollment API passing along the following:
- An access token to the MDM enrollment service (MDM application in Azure AD).
- The acceptance of terms blob obtained from the corresponding MDM page (if present).
To obtain the MDM access token it first obtains an auth code by authenticating to the MDM enrollment service application silently relying on SSO cookies obtained previously during authentication to Azure DRS:
GET https://login.microsoftonline.com/common/oauth2/authorize
?client_id=29d9ed98-a469-4536-ade2-f981bc1d605e
&msafed=0
&nonce=7912667160858957
&redirect_uri=https%3A%2F%2Flogin.microsoftonline.com%2FWebApp%2FCloudDomainJoin%2F4
&response_type=code
&resource=https://manage-beta.microsoft.com/
A few things to note:
- Client ID corresponds to the one of the MDM app in Azure AD.
- The response type requested is a code.
- The resource is the value for the mdm_enrollment_url claim in the ID token.
The worker API will then use the code to obtain an access token for enrollment to happen. It does so by posting the code to the token end-point:
POST https://login.microsoftonline.com/common/oauth2/token
grant_type=authorization_code
&code=AAABAAAAiL9Kn2Z27UubvWFPbm0gLViFwdiaP_FoKfldoPoZ6-o_aBfB1H8nQJgkWzbD1W1J8F3hCrO7aTO69QAib-PfvEFZTC3BwK53vNfuZ576-HunV5qGup1Pbf5N8p3Dv-HcZ
tMIUpgvmqwTCJ1mS4e4M-0cPaCejrV4imOrVOocm92F9TS5ME-d44PQ2aQVXqLSKfGsvUEnbnzN1InYTmkk_sHaxZOiAYPofFR4AeuM5EmaNCK3e5lQVqHaBIXmGJnRRpKxOqQAAPxXtPrh
5089NjNhEOgdkywyqCOSmV7FEejcSycZmpPHuvzxuUq85V_g0XS2a0y6OSgoh8lqSIBVXerds7etsC6USg4HMnCKxmZHrhxTjAp0citwUoGV3Ch6uOOgVpOKt7D4AZIfTYVEhz9p-7371EG
ZaIkC6jKdfg3SwxJj4gfC_WfeJc6NGCJE2iWcIQpFmywHEBAnNrQv_2ZmyTq3TEnycijLpK8IUU5ZwtqAqAFoi7qRKb0R6GlRSnqCS9WKQaRbGkzF17IENorhD01MYwWCOrtcMW4oKfy7Qw
Ehn5oPEIoaoQzLsbwjL5Wor90sbmltHCXN5KECSkMnMaa5Yz0YPsdh4ULaOZDwXrJrJxau4_DCxHss_0ENu2mjfH3tumTjG-s3A-qoHiUHjxUXbls_BdQ0X8SVLjECz1mLVsOe_rNc_bsUl
PTdAzLSyQkeRTWFYb19X3CwPCAA
&client_id=29d9ed98-a469-4536-ade2-f981bc1d605e
&redirect_uri=https%3A%2F%2Flogin.microsoftonline.com%2FWebApp%2FCloudDomainJoin%2F4
&resource=https://manage-beta.microsoft.com/
The worker API will get back a bearer token with an access token, an ID token and a refresh token. It extracts the access token and passes it to the MDM API.
MDM enrollment occurs and sign-in policy is obtained as part of the payload coming from enrollment.
(6) User signs into Windows
Once MDM enrollment completes it is time for the user to sign in. The user is automatically signed in if the tenant is managed (i.e. no federation with an on-premises STS). This is accomplished by taking the authentication buffer obtained during authentication to Azure DRS (step #2 above) and passing it to the LSA to the Cloud Authentication Provider which in turn authenticates both the user and the device with Azure AD. For more information about how this works in detail look for a future post on SSO in Windows 10 devices.
Please note that for a future update of Windows we have plans to extend the user’s automatic sign-in behavior to federated tenants as well.
(7) User provisions Microsoft Passport for Work
User provisions Microsoft Passport for Work for a secure and convenient sign-in to Windows using Windows Hello, a user’s gesture that includes a PIN, finger-print, iris or facial recognition.
I won’t delve into this today as this subject deserves a full post. Please look for a future post about Internals of Microsoft Passport for Work for complete details on what happens behind the scenes.
(8) Device encryption is enabled and BitLocker key is escrowed to Azure AD
If the device is InstantGo capable (always on, always connected, like the Surface or Surface Pro), device disk encryption is enabled and the key is sent to Azure AD to be registered in the corresponding device object.
This is done by the BitLocker Drive Encryption Service (BDESVC) which uses the following end-point discoverable through the DsrGetJoinInfoEx API to write the key in Azure AD:
POST https://enterpriseregistration.windows.net/EnrollmentService/Key
The way the service authenticates the device to the end-point is by relying on the SSO capabilities of Windows 10. For more information about this please look for a future post about SSO in Windows 10 devices.
(9) User enterprise settings are applied.
One last point to mention the Enterprise Roaming of Settings capability. As of the date of this post it is a capability that is still on Private Preview (soon will be available for Public Preview). This component will authenticate to the Enterprise Roaming of Settings service using the SSO capabilities of Windows 10.
Once authenticated settings are retrieved and applied to the local device. It will rely on information returned by DsrGetJoinInfo to reach out to the service (i.e. user_setting_sync_url claim of ID token initially obtained, accessible through the DsrGetJoinInfo API).
Final thoughts
I hope this detail gives you a good idea about how Azure AD Join works. Please also let me know what you would like to see posted in future entries.
Again thanks for reading this far :).
See you soon,
Jairo (Twitter: JairoC_AzureAD)