Office365 authentication without login redirection - authentication

I'm trying to load data from Office365 email without need for user interaction. I've created Azure App and I have Client ID and Client secret.
I also have user information (email + password).
I need to call Office365 API to download emails from mailbox. But I need application to download them in background without user interaction (redirecting to MS/Office365 login page) to get authenticated/logged into mailbox.
Is there any way how to do this only through Office API, without need of redirection?
Thanks for any info.

Yes, you are able to create a daemon service app using the Client Credential flow to authenticate the app.
Here is a code sample to retrieve the mails using Microsoft Graph SDK with this flow:
string clientId = "";
string clientsecret = "";
string tenant = "";
string resourceURL = "https://graph.microsoft.com";
string authority = "https://login.microsoftonline.com/" + tenant + "/oauth2/token";
string userMail = "user1#yourdomain.onmicrosoft.com";
var credential = new ClientCredential(clientId, clientsecret);
AuthenticationContext authContext =new AuthenticationContext(authority);
var authResult = await authContext.AcquireTokenAsync(resourceURL, credential);
var graphserviceClient = new GraphServiceClient(
new DelegateAuthenticationProvider(
(requestMessage) =>
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", authResult.AccessToken);
return Task.FromResult(0);
}));
var items = await graphserviceClient.Users[userMail].Messages.Request().OrderBy("receivedDateTime desc").GetAsync();
foreach (var item in items)
{
Console.WriteLine(item.Subject);
}
And we need to register the app on the Azure AD portal and grant the app Mail.Read scope like figure below:
Refer to here for more detail about calling Microsoft Graph in a service or daemon app

Related

Google API - Authentication setup and .NET client

I'm trying to achieve two things from my C# client application using Google API.
List all users of my google directory
Get metadata of all emails for each user
It appears, however, that my service account configuration is giving me issues. This is what I have done.
Created project in console.cloud.google.com
Enabled Admin SDK and Gmail API from API's and Services
Created Service Account with Domain-Wide delegation and obtained credentials.json file.
4. On admin.google.com I went to Security / API Controls and manage Domain-wide delegation. From here I added new API Client from my Service Account client id and assigned the following scopes:
https://www.googleapis.com/auth/gmail.settings.basic
https://www.googleapis.com/auth/gmail.settings.sharing
https://www.googleapis.com/auth/admin.directory.user.readonly
Finally, when I want to try to retrieve users list I get error: Expected OAuth 2 access token, login cookie or other valid authentication credential
static void Main(string[] args)
{
using (var stream =
new FileStream("..\\..\\..\\credentials.json", FileMode.Open, FileAccess.Read))
{
var credential = GoogleCredential.FromFile("..\\..\\..\\credentials.json");
var dirservice = new DirectoryService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "testbuildingnomiproductname",
});
var listReq = dirservice.Users.List();
Users allUsers = listReq.Execute();
int counter = 0;
foreach (User myUser in allUsers.UsersValue)
{
Console.WriteLine("*" + myUser.PrimaryEmail);
counter++;
}
Console.WriteLine(counter);
Console.ReadKey();
}
}

Call Azure API from WebJob/ Shared code between WebJob and web api

I have a web api in an ASE and an associated web job. I am trying to call this web api from the web job but it always fails with winhttpexception: a security error has occurred. I have put in all the tls related settings but still getting the error.
Any suggestions on the error?
Also is there a way to share code between WebJob and web api?
I was able to resolve the issue by setting the below in my code.This resolved the Security Error.
using(var handler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback = (sender,certificate,chain,sslPolicyErrors) => true
})
You could create a console app and publish it as Azure WebJobs. For username and password you could click Get Publish Profile in your Azure webapp overview to get them.
Then you could use the following code in Console App to call your Azure Webapi.
string userName = "$xxxxxx";
string userPassword = "xxxxxxxxxxxxx";
string webAppName = "xxxxxx";
var base64Auth = Convert.ToBase64String(Encoding.Default.GetBytes($"{userName}:{userPassword}"));
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Authorization", "Basic " + base64Auth);
var baseUrl = new Uri($"https://{webAppName}.azurewebsites.net/api/values");
var result = client.GetAsync(baseUrl).Result;
if (result.IsSuccessStatusCode)
{
var readTask = result.Content.ReadAsStringAsync();
readTask.Wait();
var value = readTask.Result;
Console.WriteLine(value.ToString());
}
}
Console.WriteLine("run successfully");
Output as below:

How to fetch email in web api 2 which is secured by WAAD

I am using MVC 5 client which is secured by "UseOpenIdConnectAuthentication" and getting all user details in Claims object, this client is calling WAAD secured Web Api by "Bearer" authentication token.
I need to fetch username or email in the web api. I tried different options but nothing worked.
I am getting null in Identity.Name, other properties I am getting like nameidentifier, objectidentifier, tenanted etc.
Please advise.
Thanks
Below code I am using for access token in Web Client.
string signedInUserID = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
string tenantID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
ClientCredential clientcred = new ClientCredential(Startup.clientId, Startup.appKey);
AuthenticationContext authenticationContext = new AuthenticationContext(Startup.aadInstance + Startup.tenantId, new ADALTokenCache(signedInUserID));
AuthenticationResult authenticationResult = authenticationContext.AcquireToken(apiResourceId, clientcred);
return authenticationResult.AccessToken;
Start up Code
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = Authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
UseTokenLifetime = false,
Notifications = new OpenIdConnectAuthenticationNotifications()
{
// If there is a code in the OpenID Connect response, redeem it for an access token and refresh token, and store those away.
AuthorizationCodeReceived = (context) =>
{
var code = context.Code;
ClientCredential credential = new ClientCredential(clientId, appKey);
string signedInUserID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
AuthenticationContext authContext = new AuthenticationContext(Authority, new ADALTokenCache(signedInUserID));
AuthenticationResult result = authContext.AcquireTokenByAuthorizationCode(
code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, graphResourceId);
return Task.FromResult(0);
}
Below are the Token Details:
You can get the upn of current user by :
var upn = ClaimsPrincipal.Current.FindFirst("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn").Value;
Other way is getting user's basic information using Microsoft Graph api , please refer to On-Behalf-Of scenario .The OAuth 2.0 On-Behalf-Of flow serves the use case where an application invokes a service/web API, which in turn needs to call another service/web API. Please refer to protocol explanation and code sample .
Update :
Looking into your codes , you are using client credential flow to acquire token for your web api :
AuthenticationResult authenticationResult = authenticationContext.AcquireToken(apiResourceId, clientcred);
The OAuth 2.0 Client Credentials Grant Flow permits a web service (confidential client) to use its own credentials instead of impersonating a user, to authenticate when calling another web service. That's why you can't get upn information which associated with a user .
You can use authorization code flow with user's identity , Please refer to code sample :
string userObjectID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
AuthenticationContext authContext = new AuthenticationContext(Startup.Authority, new NaiveSessionCache(userObjectID));
ClientCredential credential = new ClientCredential(clientId, appKey);
result = await authContext.AcquireTokenSilentAsync(todoListResourceId, credential, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));

How to delegate Identity from Web-Application to WebAPI

I am trying to build a website, where the user logs in at the and can use an backend web-API.
Calls to the backend web-API will always be proxied by the frontend website, since the backend is not publicly available.
Back- and frontend are MVC 6 (or MVC Core?) projects based on ASP.net Core.
The frontend currently authenticates (successfully) by using OpenId-Connect.
The backend should use JwtBearerToken.
The authentication so far requests the response type is id_token code and the scope is openid profile.
After the roundtrip to the Auth-Server (ADFS 2016), I will end up in the AuthorizationCodeReceived-Event from ASP.NET, but I have no luck in exchanging the code for authorization token. I tried the following using ADAL:
public override async Task AuthorizationCodeReceived(AuthorizationCodeReceivedContext context)
{
await base.AuthorizationCodeReceived(context);
var clientCredential = new ClientCredential(context.Options.ClientId, context.Options.ClientSecret);
var oAuthContext = new AuthenticationContext(context.Options.Authority, false);
var oAuthResult = await oAuthContext.AcquireTokenByAuthorizationCodeAsync(context.Code, new Uri(context.RedirectUri), clientCredential);
}
I had to disable the authority validation (which I do not like) and I do not get results other than Http-Status 400.
I'd be happy for any advice how to move on.
Update
Further Investigation Shows, that the OpenIdConnect-Configuration allows to save auth and refresh Tokens into the Claims. Nevertheless I don't see the possibility to convert it in the first place.
I also tried exchanging the code by hand (PS: Invoke-WebRequest ...) but had no success. Perhaps this is a problem of ADFS TP4...
I've managed to get this scenario to work with TP4.
AuthorizationCodeReceived = async n =>
{
string code = n.Code;
AuthenticationContext ac = new AuthenticationContext(BaseAddress, false);
ClientCredential client = new ClientCredential("clientid", "secret");
string resourceId = "https://myservices/myapi";
AuthenticationResult ar = await ac.AcquireTokenByAuthorizationCodeAsync(code, new Uri("https://localhost:44300/"), client, resourceId);
}
You can then use the access token from a controller method like this:
AuthenticationContext ac = new AuthenticationContext(Startup.BaseAddress, false);
ClientCredential cred = new ClientCredential("clientid", "secret");
string resourceId = "https://myservices/myapi";
AuthenticationResult ar = ac.AcquireTokenSilent(resourceId, cred, UserIdentifier.AnyUser);
var client = new HttpClient();
client.SetBearerToken(ar.AccessToken);
var result = await client.GetStringAsync("http://localhost:2727/identity");

What is the easiest way to detect whether a user is logged in via google apps marketplace?

I have a web application which users log into using google oauth2.
I have a google apps marketplace listing for this application, and some portion of the google oauth2 users didn't need to grant permission to my application because their google apps domain administrator did it while installing the apps marketplace listing.
I'd like to be able to detect this second group of users, to analyze how frequently the apps marketplace listing is being used to log into my application. At the moment all google oauth2 logins look the same to my application.
Is there a simple API call I can make to find out whether the current user is in this group?
I use this code for finding out marketplace listing info for given appId and target domain:
InputStream p12File = Config.class.getResourceAsStream(Config.SERVICE_ACCOUNT_PRIVATE_KEY_RESOURCE_PATH);
PrivateKey serviceAccountPrivateKey = SecurityUtils.loadPrivateKeyFromKeyStore(SecurityUtils.getPkcs12KeyStore(), p12File, "notasecret", "privatekey", "notasecret");
JsonFactory jsonFactory = new JacksonFactory();
HttpTransport t = GoogleNetHttpTransport.newTrustedTransport();
GoogleCredential.Builder bgc = new GoogleCredential.Builder()
.setTransport(t)
.setJsonFactory(jsonFactory)
.setServiceAccountScopes(Collections.singleton("https://www.googleapis.com/auth/appsmarketplace.license"))
.setServiceAccountPrivateKey(serviceAccountPrivateKey)
.setServiceAccountId(Config.SERVICE_ACCOUNT_ID);
GoogleCredential gc = bgc.build();
String token = gc.getAccessToken();
if(token == null) {
gc.refreshToken();
token = gc.getAccessToken();
}
HttpGet request = new HttpGet("https://www.googleapis.com/appsmarket/v2/customerLicense/" + applicationId + "/" + customerDomain);
request.setHeader("Authorization", "Bearer " + token);
DefaultHttpClient client = new DefaultHttpClient(httpParams);
HttpResponse resp = client.execute(request);
// ... read API JSON response