var outlookServicesClient = await AuthenticationHelper.EnsureOutlookServicesClientCreatedAsync("Calendar");
internal static async Task<OutlookServicesClient> EnsureOutlookServicesClientCreatedAsync(string capabilityName)
{
var signInUserId = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
var userObjectId = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
AuthenticationContext authContext = new AuthenticationContext(SettingsHelper.Authority, new ADALTokenCache(signInUserId));
try
{
DiscoveryClient discClient = new DiscoveryClient(SettingsHelper.DiscoveryServiceEndpointUri,
async () =>
{
var authResult = await authContext.AcquireTokenSilentAsync(SettingsHelper.DiscoveryServiceResourceId, new ClientCredential(SettingsHelper.ClientId, SettingsHelper.ClientSecret),
new UserIdentifier(userObjectId, UserIdentifierType.UniqueId));
return authResult.AccessToken;
});
var dcr = await discClient.DiscoverCapabilityAsync(capabilityName);
return new OutlookServicesClient(dcr.ServiceEndpointUri,
async () =>
{
var authResult = await authContext.AcquireTokenSilentAsync(dcr.ServiceResourceId,
new ClientCredential(SettingsHelper.ClientId, SettingsHelper.ClientSecret),
new UserIdentifier(userObjectId, UserIdentifierType.UniqueId));
return authResult.AccessToken;
});
}
catch (AdalException exception)
{
//Handle token acquisition failure
if (exception.ErrorCode == AdalError.FailedToAcquireTokenSilently)
{
authContext.TokenCache.Clear();
throw exception;
}
return null;
}
public ADALTokenCache(string user)
{
// associate the cache to the current user of the web app
User = user;
this.AfterAccess = AfterAccessNotification;
this.BeforeAccess = BeforeAccessNotification;
this.BeforeWrite = BeforeWriteNotification;
// look up the entry in the DB
Cache = db.UserTokenCacheList.FirstOrDefault(c => c.webUserUniqueId == User);
// place the entry in memory
this.Deserialize((Cache == null) ? null : Cache.cacheBits);
}
i am using this code for ADAL authentication. This is working fine in my local IIS server. When i hosted the same on AZURE VM then getting an error like
"Failed to acquire token silently. Call method AcquireToken". Can anybody help me on resolving this error??
Settings Helper code as follows. In public ADALTokenCache(string user) we are getting userid finely but getting an empty cache... What will be the reason??
AuthenticationContext authContext = new AuthenticationContext(SettingsHelper.Authority, new ADALTokenCache(signInUserId));
try
{
DiscoveryClient discClient = new DiscoveryClient(SettingsHelper.DiscoveryServiceEndpointUri,
async () =>
{
var authResult = await authContext.AcquireTokenSilentAsync(SettingsHelper.DiscoveryServiceResourceId,
new ClientCredential(SettingsHelper.ClientId,
SettingsHelper.ClientSecret),
new UserIdentifier(userObjectId,
UserIdentifierType.UniqueId));
return authResult.AccessToken;
});
Make sure that your authority does not contain "common". Also, please turn on the diagnostics as explained in http://www.cloudidentity.com/blog/2015/08/07/adal-diagnostics/ and take a look at the trace. Very often this is due to a mismatch in the cache - acquiretokensilent only works with cached tokens, and if you didn't seed the cache/you are not working against the cache instance you selected earlier/you pass a different user identifier/you pass common as authority you'll get a cache miss.
I assume you were using the O365-ASPNETMVC-Start project on Github.
What's the "ida:TenantId" setting in your web.config file on Azure VM?
I can get the same error "Failed to acquire token silently. Call method AcquireToken" if setting the "ida:TenantId" to "common". For this scenerio, you need to set "ida:TenantId" to actual tenant id. For example, "e07xxxx0e-fxx2-441f-ad9a-9dxxa59xxx52" (guid).
Related
I have created a windows app which is calling some app hosted in azure. App service is using AAD for authentication.
Following is the method I am using for MS login and storing token.
authContext = new AuthenticationContext(authority, new FileCache());
authContext.AcquireTokenAsync(todoListResourceId, clientId, redirectUri, new PlatformParameters(PromptBehavior.Always)).ContinueWith(t =>
{
result = t.Result;
})
.Wait();
By using this above method, I am successfully able to login using MS credential and getting access token from result.
Now I am passing this token in header request to get some data from app which has SSO enabled like :-
using (var client = new HttpClient())
{
var uri = "http://appservice.azurewebsites.net/api/values/5";
_auth.GetToken().ContinueWith(t => { token = t.Result; }).Wait();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var TaskAPI = client.GetAsync(uri).ContinueWith(task =>
{
if (task.Status == TaskStatus.RanToCompletion)
{
var response = task.Result;
if (response.IsSuccessStatusCode)
{
flag = 1;
var data = response.Content.ReadAsStringAsync().Result;
}
}
});
TaskAPI .Wait();
}
Get Token function is acquiring token silently
Below is GetToken() used to fetch token for calling API
authContext.AcquireTokenSilentAsync(todoListResourceId, clientId)
.ContinueWith(i =>
{
result = i.Result;
}).Wait();
return result.AccessToken;
When I call this URI by passing retrieved token, I get response of Un Authorized(401).
How can I check if token is proper or I am missing something or if there is any other way to do that?
Thanks
Subham,Nathcorp
Hello I have developed a Microsoft application using Microsoft Graph API in order to obtain planner data and store it in a database for now. On it's own the application works fine without any issue what so ever.
The next task for me is to integrate this separate application into the main company application. The main company's website uses form authentication. What is the best way to integrate this. Currently when I try to login to get authorized I am redirected to the form login not the Microsoft one
I have registered the application in the Microsoft application registration pool. I have also added the office 365 api
This is the token obtain code that i am using
public async Task<string> GetUserAccessTokenAsync()
{
string signedInUserID = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
tokenCache = new SessionTokenCache(
signedInUserID,
HttpContext.Current.GetOwinContext().Environment["System.Web.HttpContextBase"] as HttpContextBase);
//var cachedItems = tokenCache.ReadItems(appId); // see what's in the cache
ConfidentialClientApplication cca = new ConfidentialClientApplication(
appId,
redirectUri,
new ClientCredential(appSecret),
tokenCache);
try
{
AuthenticationResult result = await cca.AcquireTokenSilentAsync(scopes.Split(new char[] { ' ' }));
return result.Token;
}
// Unable to retrieve the access token silently.
catch (MsalSilentTokenAcquisitionException)
{
HttpContext.Current.Request.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties() { RedirectUri = "/" },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
throw new Exception(Resource.Error_AuthChallengeNeeded);
}
}
This is the sign in method I am trying use when trying to directly log in
// Signal OWIN to send an authorization request to Azure.
HttpContext.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties { RedirectUri = "/" },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
I have solved this issue by implementing the following code
public ActionResult SignIn()
{
var authContext = new AuthenticationContext("https://login.microsoftonline.com/common");
string redirectUri = Url.Action("Authorize", "Planner", null, Request.Url.Scheme);
Uri authUri = authContext.GetAuthorizationRequestURL("https://graph.microsoft.com/", SettingsHelper.ClientId,
new Uri(redirectUri), UserIdentifier.AnyUser, null);
// Redirect the browser to the Azure signin page
return Redirect(authUri.ToString());
}
public async Task<ActionResult> Authorize()
{
// Get the 'code' parameter from the Azure redirect
string authCode = Request.Params["code"];
AuthenticationContext authContext = new AuthenticationContext(SettingsHelper.AzureADAuthority);
// The same url we specified in the auth code request
string redirectUri = Url.Action("Authorize", "Planner", null, Request.Url.Scheme);
// Use client ID and secret to establish app identity
ClientCredential credential = new ClientCredential(SettingsHelper.ClientId, SettingsHelper.ClientSecret);
try
{
// Get the token
var authResult = await authContext.AcquireTokenByAuthorizationCodeAsync(
authCode, new Uri(redirectUri), credential, SettingsHelper.O365UnifiedResource);
// Save the token in the session
Session["access_token"] = authResult.AccessToken;
return Redirect(Url.Action("Index", "Planner", null, Request.Url.Scheme));
}
catch (AdalException ex)
{
return Content(string.Format("ERROR retrieving token: {0}", ex.Message));
}
}
A link to the solution that helped tackle this was this. It's slightly old but still helped out massively
https://www.vrdmn.com/2015/05/using-office-365-unified-api-in-aspnet.html
I'm having an issue with the AcquireTokenSilentAsync method and was hoping anyone could help me out.
In the method below im trying to use the AcquireTokenSilentAsync method so I can use it later to make a call to the Microsoft graph api. Unfortunately the Users property of the ConfidentialClientApplication is empty, as a result of that the cca.AcquireTokenSilentAsync fails because it requires the first user in the parameter by calling cca.Users.First().
public async Task<string> GetUserAccessTokenAsync()
{
string signedInUserID = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
HttpContextWrapper httpContext = new HttpContextWrapper(HttpContext.Current);
TokenCache userTokenCache = new SessionTokenCache(signedInUserID, httpContext).GetMsalCacheInstance();
ConfidentialClientApplication cca = new ConfidentialClientApplication(
appId,
redirectUri,
new ClientCredential(appSecret),
userTokenCache,
null);
try
{
AuthenticationResult result = await cca.AcquireTokenSilentAsync(scopes.Split(new char[] { ' ' }), cca.Users.First());
return result.AccessToken;
}
// Unable to retrieve the access token silently.
catch (Exception)
{
HttpContext.Current.Request.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties() { RedirectUri = "/" },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
throw new ServiceException(
new Error
{
Code = GraphErrorCode.AuthenticationFailure.ToString(),
Message = Resource.Error_AuthChallengeNeeded,
});
}
}
}
I'm not sure why the ConfidentialClientApplication doesn't contain any Users, as the sign in at the start of the application works correctly. I am only using UseCookieAuthentication and UseOpenIdConnectAuthentication in the startup.
I hope anyone can help me with this problem!
So, here is the problem. I searched all over the MSDN and here on Stack but there isn't one definitive answer how to get (or is it even possible as of today?) to access Shared calendars in Office365.
I followed this tutorial and here is offending method:
public async Task<ActionResult> Index()
{
List<MyCalendar> myCalendars = new List<MyCalendar>();
var signInUserId = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
var userObjectId = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
AuthenticationContext authContext = new AuthenticationContext(SettingsHelper.Authority, new ADALTokenCache(signInUserId));
try
{
DiscoveryClient discClient = new DiscoveryClient(SettingsHelper.DiscoveryServiceEndpointUri,
async () =>
{
var authResult = await authContext.AcquireTokenSilentAsync(SettingsHelper.DiscoveryServiceResourceId, new ClientCredential(SettingsHelper.ClientId, SettingsHelper.AppKey), new UserIdentifier(userObjectId, UserIdentifierType.UniqueId));
return authResult.AccessToken;
});
var discoveryCapabilitiesResult = await discClient.DiscoverCapabilitiesAsync();
var dcr = await discClient.DiscoverCapabilityAsync("Calendar");
OutlookServicesClient exClient = new OutlookServicesClient(dcr.ServiceEndpointUri,
async () =>
{
var authResult = await authContext.AcquireTokenSilentAsync(dcr.ServiceResourceId, new ClientCredential(SettingsHelper.ClientId, SettingsHelper.AppKey), new UserIdentifier(userObjectId, UserIdentifierType.UniqueId));
return authResult.AccessToken;
});
//var calendarsResult = await exClient.Me.Calendars.ExecuteAsync();
var calendarsResult = await exClient.Me.Calendars.ExecuteAsync();
do
{
var calendars = calendarsResult.CurrentPage;
foreach (var c in calendars)
{
myCalendars.Add(new MyCalendar { DisplayName = c.Name });
}
calendarsResult = await calendarsResult.GetNextPageAsync();
} while (calendarsResult != null);
}
catch (AdalException exception)
{
//handle token acquisition failure
if (exception.ErrorCode == AdalError.FailedToAcquireTokenSilently)
{
authContext.TokenCache.Clear();
//handle token acquisition failure
}
}
return View(myCalendars);
}
This function will return only Calendar under "My Calendars" but not the others (see the picture)
I get only the first one - that is mine calendar. Second one under "My Calendars" is shared with me (I'm not the author, somebody else is and I have r/w on it) and one under "Shared Calendars" is company-wide one (on this one I also have r/w permissions).
Is there a way to get all of them? On portal.azure.com my app is added and I set permissions for both mine and shared calendars:
I have no idea what to do. Contacts are working properly but I cannot find a way to get any shared calendar.
Based on the test, the Microsoft Graph could get the all calendars. Here is the rest for your reference:
Shared calendar from chenchenLi to NanYu:
Query the calendars from NanYu:
More detail about the Microsoft Graph REST to get the calendars, you can refer the link below:
List calendars
I am currently building an Office365 Website which manages your files within onedrive and your sharepoint files. Currently though I am having an issue in that if the sharepoint isn't a root address one (i.e. https://mysharepoint.sharepoint.com) but is a seperate one (i.e. https://intergendev1.sharepoint.com/anothersite or https://intergendev1.sharepoint.com/sites/moarsite) I cannot acquire the token silently, in other words I cannot use my token from the Office API to acquire a token for the sharepoint site. My theory is that I am using the incorrect ResourceId or ServiceEndpointUri, currently I just use https://intergendev1.sharepoint.com/anothersite for both, and I am un-able to aquire the token, but it DOES work for https://intergendev1.sharepoint.com/.
Thanks for the help in advance. If it helps, here is my AquireToken code:
internal static async Task<SharepointApiClient> GetExtenalSharepoint(Uri serviceEndpointUri,
string serviceResourceId)
{
var signInUserId = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
var userObjectId = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
AuthenticationContext authContext = new AuthenticationContext(SettingsHelper.AzureADAuthority, new AdTokenCache.AdTokenCache(signInUserId));
try
{
return new SharepointApiClient(serviceEndpointUri,
async () =>
{
var authResult = await authContext.AcquireTokenSilentAsync(serviceResourceId,
new ClientCredential(SettingsHelper.ClientId,
SettingsHelper.AppKey),
new UserIdentifier(userObjectId,
UserIdentifierType.UniqueId));
return authResult.AccessToken;
})
{
ResourceId = serviceResourceId
};
}
catch (AdalException exception)
{
//Partially handle token acquisition failure here and bubble it up to the controller
if (exception.ErrorCode == AdalError.FailedToAcquireTokenSilently)
{
authContext.TokenCache.Clear();
throw exception;
}
return null;
}
}