Server-to-Server (S2S) authentication (only support Dynamics 365 online) was great, it use ClientId/SecretKey to authentication (instead use UserName/Password) and it use without Dynamics 365 license.
But everybody have problem when use CllientId/SecretKey, the main problem is HTTP Error 401 - Unauthorized: Access is denied
I headache this problem more than 3 days, and now, I succeed authentication with ClientId/SecretKey.
This post I will fully explain you how I can achieve it.
Important noted: do the correct order step by step here
1. Register App with Azure Active Directory
After you register App, save these value here to use it later
- Application ID
- Url redirect
- SecretKey
Save Application ID
Save Redirect URI
Save SecretKey. Rember the SecretKey only show once.
When register App use Application Type = Native and Assign Permission to Dynamics CRM Online
2. Create new Office365 user
We call this user as ApplicationUser
- Assign CRM Online license
- Role: Dynamics 365 Service Administrator
Create new Office 365 user with Dynamics 365 license and role
3. Create a custom security role and assgin to ApplicationUser
Open Dynamics 365 by CRM Administrator account
- Clone a System Administrator security role to new ApplicationUserRole
- Assign ApplicationUserRole to ApplicationUser user
Clone System Administrator role to ApplicationUserRole
Assign ApplicationUserRole to ApplicationUser
-
Open Application User form for this ApplicationUser
- Change form from User to Application User (if not change)
- Update field Application ID from step 1.a (Application ID)
- Save and go back to Enabled Users
Update Application ID for ApplicationUser
-
Change view to Application Users and check field Application ID URI for ApplicationUser empty
Make sure Application ID URI of ApplicationUser is emtpy
4. Remove Dyanmics 365 license
Back to 365 admin and remove Dynamics CRM license from ApplicationUser you already do in step 2
S2S Authentication no need a license, if you assign license to ApplicationUser it not work.
5. Manually build url and use it once
The syntax url here
https://login.microsoftonline.com/{talent-id}/oauth2/authorize?client_id={application-id}&response_type=code&redirect_uri={url-redirect}&response_mode=query&resource={crm-url}&state={new-guid}
- talent-id: your azure active directory id. Azure portal -> Azure Active Directory -> Properties -> Directory ID
- application-id: step 1.a
- url-redirect: step 1.b
- crm-ur: your full Dynamics 365 url, E.g.: https://abcd.crm.dynamics.com
- new-guid: generator a new GUID
My final manually url here:
https://login.microsoftonline.com/12b5c856-99c8-4268-b99f-b5bfd02ae0f3/oauth2/authorize?client_id=2015d711-986e-4728-87d5-228994e190ba&response_type=code&redirect_uri=http://myapplicationusers.com&response_mode=query&resource=https://abcd.crm.dynamics.com&state=5cebc804-b7d6-4e26-b850-ed45708f37fa
6. Grant ApplicationUser to App
- Use the manually url build on step 5. Open IE private window and paste the url to IE address bar
- Process first time login (and only do it once) with the ApplicationUser you create in step 2, it redirect to url-redirect. Leave it and close your IE browser.
First and only once login to grant permission to ApplicationUser
7. Check Application ID URI
Goback to Application Users view and check the Application ID URI (step 3.e.1) now have value same value with Applcation ID
The key I found here, if this field NULL, 401 return.
8. Build console application
- Create a console application
- Use code below and now you can see WhoAmI
Note: you can use authenticationResult.AccessToken for WebAPI call in the header request
class Program
{
static private Uri GetServiceUrl(string organizationUrl)
{
return new Uri(organizationUrl + @"/xrmservices/2011/organization.svc/web?SdkClientVersion=8.2");
}
static void Main(string[] args)
{
var organizationUrl = "https://phuoc2017mar26.crm.dynamics.com";
var aadInstance = "https://login.microsoftonline.com/";
var tenantID = "12b5c856-99c8-4268-b99f-b5bfd02ae0f3";
var clientId = "2015d711-986e-4728-87d5-228994e190ba";
var appKey = "your-private-secret-key-here";
var clientcred = new ClientCredential(clientId, appKey);
var authenticationContext = new AuthenticationContext(aadInstance + tenantID);
var authenticationResult = authenticationContext.AcquireToken(organizationUrl, clientcred);
var requestedToken = authenticationResult.AccessToken;
using (var sdkService = new OrganizationWebProxyClient(GetServiceUrl(organizationUrl), false))
{
sdkService.HeaderToken = requestedToken;
var request = new OrganizationRequest()
{
RequestName = "WhoAmI"
};
var response = sdkService.Execute(new WhoAmIRequest()) as WhoAmIResponse;
Console.WriteLine(response.UserId);
Console.ReadKey();
}
}
}
Run the code and I get the result here
I get UserID GUID of ApplicationUser we already grant permission to App
GREAT
Some mistakes
- Don’t assign Application ID to user.
- Don’t create a custom security role and assign to application user, everybody always use default System Administrator role and assign to application user.
- Build correct url above but login with another user that have assign Application ID, or login with user don’t assign application key
- Forgot remove license from user before use url above.
- Manually url not match with url-redirect when register application in Azure Active Directory
- Don’t do correct order I list here
Finally your target
- Application User should in Application Users view
- Application User should have 3 field (not blank)
- Application ID
- Application ID URI
- Azure AD Object ID
- Application User should remove Dynamics 365 license
- Application User must have a custom security role
Hope this post working for you.