Updating emails in a Shared Mailbox using an Azure Client Application and Microsoft Graph SDK - microsoft-graph-sdks

I'm trying to update email categories of items in a Shared Mailbox using Microsoft Graph SDK.
This is the code used to create the Graph SDK Client. It uses the Azure Client Application credentials (ClientId, TenantId, Secret):
var handlers = GraphClientFactory.CreateDefaultHandlers(new DelegateAuthenticationProvider(async msg =>
{
var accessToken = await GetAccessTokenFromCacheOrRefresh(cancellationToken);
msg.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
}));
handlers.Add(_graphLoggingHandler);
var httpClient = GraphClientFactory.Create(handlers);
_graphServiceClient = new GraphServiceClient(httpClient);
This is the code used to update the message categories:
var updatedMessage = new Message();
updatedMessage.Categories = CreateMessageCategories();
await _graphServiceClient.Users[emailAddress].Messages[emailId]
.Request()
.UpdateAsync(updatedMessage, cancellationToken);
When I run this code, I get the error "Access Denied". What permissions do I need to update messages in this Shared MailBox? The Administrators tell me I can't use Mail.ReadWrite on the Application level because that will allow the application to modify emails in all mailboxes.

When the app is configured with application permissions, then the app has access to all mailboxes by default.
Tenant administrators can create an application access policy by using the New-ApplicationAccessPolicy PowerShell cmdlet to limit an app to only specific mailboxes and not all Exchange Online mailboxes in the organization.
More details:
Limiting application permissions to specific Exchange Online mailboxes

Related

Connecting Google add-on to google vm instance

I am developing an add-on for google drive.
As a part of the functionality of this add-on, I would like to incorporate a google vm server that performs some processing on a user's google drive files (i.e. you click on a file through the add-on, send a request with a download link to the server, the server downloads the file, then finally responds to the request with some helpful information about the file). Both the apps script for the add-on and the vm instance are connected to the same google "project."
I am struggling with google's OAuth2.0 system and how I can connect the authorization of the add-on and the vm instance together.
Currently, when users open the add-on for the first time, they are brought to the authorization screen like so .
Once they authorize, my add on has access to all the expected scopes, including read access to google drive files.
Now I want my server to have access to them as well. Unfortunately, I do not understand how to do this.
I have tried simply requesting the url returned from file.getDownloadUrl() in python. While the request returns a status code of 200, I cannot seem to get the file to download.
I have also looked into the Google Drive API for python (I am running a flask server). Unfortunately, it appears that I need an entirely new authorization flow to make it work.
Any clarity on this issue would be greatly appreciated. Frankly, I find google's documentation on this matter very scattered and confusing. So, even knowing the right place to look would extremely helpful.
Much Thanks!
EDIT
I am adding some additional code to help provide some clarity. This is currently how I make a request to my server from the add-on:
var route = http://exampleurl.com/process
var data = {
'oAuthToken': ScriptApp.getOAuthToken(),
'stateToken': ScriptApp.newStateToken().withTimeout(120).createToken(),
'fileId': e.parameters.fileId,
'fileType': e.parameters.fileMimeType
};
var options = {
'method' : 'post',
'payload' : data
};
var response = UrlFetchApp.fetch(route, options);
This code successfully sends information to my vm instance running my server.
Now, I need to authorize the server to download the file specified by fileId.
When developing, I closely followed this tutorial to set up OAuth2.0 access to the Drive API. Here are two key routes:
#app.route('/google/login')
#no_cache
def login():
session = OAuth2Session(CLIENT_ID, CLIENT_SECRET,
scope=AUTHORIZATION_SCOPE,
redirect_uri=AUTH_REDIRECT_URI)
uri, state = session.create_authorization_url(AUTHORIZATION_URL)
flask.session[AUTH_STATE_KEY] = state
flask.session.permanent = True
return flask.redirect(uri, code=302)
#app.route('/google/auth')
#no_cache
def google_auth_redirect():
req_state = flask.request.args.get('state', default=None, type=None)
if req_state != flask.session[AUTH_STATE_KEY]:
response = flask.make_response('Invalid state parameter', 401)
return response
session = OAuth2Session(CLIENT_ID, CLIENT_SECRET,
scope=AUTHORIZATION_SCOPE,
state=flask.session[AUTH_STATE_KEY],
redirect_uri=AUTH_REDIRECT_URI)
oauth2_tokens = session.fetch_access_token(
ACCESS_TOKEN_URI,
authorization_response=flask.request.url)
flask.session[AUTH_TOKEN_KEY] = oauth2_tokens
return flask.redirect(BASE_URI, code=302)
Is there a way to plug in the two tokens I generate from the add-on into this Oauth flow? It appears that Google isn't anticipating this setup given that I am required to provide a redirect URL, which wouldn't make much sense in the case of my add-on/server tech stack.
Currently, you can successfully send the access token from the Apps Script project (retrieved with getOAuthToken()), to the flask server.
Since you already got the access token, you don't need to go through all the OAuth process as defined here (use the application credentials to request the access token, provide user consent, redirect, etc.). Sending the access token through the API request is actually the last step in the OAuth flow.
You just need to use the token to build the service, and the server will be able to access the file.
Using access token to build service:
from googleapiclient.discovery import build
import google.oauth2.credentials
ACCESS_TOKEN = requestBody['oAuthToken'] # Data coming from Apps Script via UrlFetch
creds = google.oauth2.credentials.Credentials(ACCESS_TOKEN)
drive_service = build('drive', 'v3', credentials=creds) # Build Drive service
Using Drive service to download files:
After you've build the Drive service to access the API, you can use this code sample to download Google Documents, or this one for other files (both examples include a tab with a Python sample).
Note:
You might need to Install the Google Client Library if you haven't done so.
Reference:
Using OAuth 2.0 to Access Google APIs

Azure Function using MSI - Error Requesting Token

I have a Function in Azure, which has MSI (Managed Service Identity) enabled which I am trying to use to access an Azure based WebAPI (App Service WebApp) which in turn has Azure AD Authentication enabled (all same Azure Directory).
My WebAPI has an Azure App registered so it can use AAD Authentication.
This app also has the necessary AppRoles configured in its Manifest (for types 'User' and for 'Application').
I have also verified that the Functions Identity (app) was successfully created in Azure AD when I enabled MSI on the Function.
When I try to obtain a token within my Function using MSI i receive a 400 Bad Request response / error:
"ExceptionMessage": "AADSTS50105: Application '###' is not assigned to a role for the application '###'
"ErrorCode": "invalid_grant"
I have ensured the Resource value I pass in is my webAPIs app ID URI.
string resource = "<My App URI>";
string apiversion = "2017-09-01";
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("Secret", Environment.GetEnvironmentVariable("MSI_SECRET"));
var r = await client.GetAsync(String.Format("{0}/?resource={1}&api-version={2}", Environment.GetEnvironmentVariable("MSI_ENDPOINT"), resource, apiversion));
return r;
But I still get the same error. The code for requesting a token is fine, and the error does infact point towards a permissions issue.
The one thing I have not been able to do (and I guess this is the problem) is find a way to give add the new MSI/Function Identity to the Users & Groups of the webAPIs Azure App. No matter what I try my Functions App Identity does not appear in the Users list when I search for it to add as a member of the webAPI app (with the Application role).
Does anyone have any suggestions as to why I cannot add the Functions MSI to an Apps Users & Groups or to an Azure AD Group?
Or am I doing something else wrong perhaps?
Juuna was spot on in his response to my original post:
When enabling MSI on a service only a Service Principal is created in Azure AD and as such this wont appear in search results when trying to add the SP as a member of a Group or to User & Groups of an Azure AD App.
In order to assign your MSI created Service Principal permissions to your App you need to:
Edit your apps manifest and ensure you have app roles with allowed member type = "Application"
Run the PowerShell cmdlet "New-AzureADServiceAppRoleAssignment (link) to grant your Service Principal the Application role you added to your apps manifest
Within your MSI enabled service re-try requesting a token
For me, following the above, I can now successfully request app tokens from my Function so my same Azure Function can call my WebApp (which is AAD Authentication enabled).
In fact, when you enable MSI, it will create a service principal(not a user), so you could not find it in Users&Groups.
You could find it on Azure Portal and try to give permissions you need. Azure Active Directory -->Enterprise applications
Note: The service principal name is same with your function name.
In function, you should use the following code to get token, please refer to this link.
using Microsoft.Azure.Services.AppAuthentication;
using Microsoft.Azure.KeyVault;
// ...
var azureServiceTokenProvider = new AzureServiceTokenProvider();
string accessToken = await azureServiceTokenProvider.GetAccessTokenAsync("https://management.azure.com/");
// OR
var kv = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));

must I click "Allow Acess" every time I connect? Google Drive SDK API

I wrote calss that saves refresh and access token in my db. I can generate access token by refresh token too. everything works well. I working with web application. now I have another question:
1)
For example, I have already saved Refresh token. then if another person comes, I should check if that person have refresh token in my database to generate her access token. but I should have her user ID firstly.
2)
to get user ID I need her credentials
For example:
Oauth2.Builder builder = new Oauth2.Builder(new NetHttpTransport(), new JacksonFactory(), credentials);
Oauth2 userInfoService = builder.build();
Userinfo userInfo= userInfoService.userinfo().get().execute();
3)
and to get credentials I need authorizationCode:
Credential credentials = exchangeCode(authorizationCode);
4)
and to get authorizationCode User Should Click "Allow Acess" in order to retrieve that code? is it right?
for example:
HttpTransport httpTransport = new NetHttpTransport();
JacksonFactory jsonFactory = new JacksonFactory();
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(jsonFactory, GoogleStorage.class.getResourceAsStream(CLIENTSECRETS_LOCATION));
flow = new GoogleAuthorizationCodeFlow.Builder(httpTransport, jsonFactory, clientSecrets, SCOPES).setAccessType("offline").setApprovalPrompt("force").build();
and get URL where autorization code will be retrieved:
flow.newAuthorizationUrl().setRedirectUri(REDIRECT_URI).build();
for instance URL:
https://accounts.google.com/o/oauth2/auth?access_type=offline&approval_prompt=force&client_id=695230079990-bus3d9gp31vvv64qq5n4ma9mk8vtc1ck.apps.googleusercontent.com&redirect_uri=http://localhost/oauth2callback&response_type=code&scope=https://www.googleapis.com/auth/drive.file%20https://www.googleapis.com/auth/userinfo.email%20https://www.googleapis.com/auth/userinfo.profile
must one person click that button everytime she try to connect to Drive API? I see web applications, where one person clicks that button only once. how to get Autorization code without every time clicking "Allow Acess". I need to click that only once.
Users do not have to grant access to the app every time they run it. You store their credentials in a database and reuse them when needed.
Check the Java DrEdit guide for a complete sample app showing how to get a complete set of credentials for every request:
https://developers.google.com/drive/examples/java

Need to create object of google DriveService from picasa_consumer_key, picasa_consumer_secret, authtoken, authsecret

I had one winform application which used to upload photos to picasa. I had used oAuth authentication and user grants permission for the following
Picasa Web Albums
Profile Information
I store authentication token for later use. This works perfectly.
Now I want to extend it to include GOOGLE DRIVE also, so what I have done is I have added scope to Authentication as below
By file Drive API
So now user grants Permission for all three, i.e. Picasa, Profile info and Google Drive.
But I am not able to upload photos to google drive, reason being I dont know how to create object of Google Drive Service.
for Picasa I used code as below
OAuthParameters parameters = new OAuthParameters()
{
ConsumerKey = CONSUMER_KEY,
ConsumerSecret = CONSUMER_SECRET,
Token = AuthToken,
TokenSecret = AuthSecret
};
requestFactory = new GOAuthRequestFactory("XXXX", "XXXX", parameters);
service = new PicasaService(requestFactory.ApplicationName);
Is there anything similar for google drive?
I need to create object DriveService(auth) ..
In short I need to know how can I create OBJECT of DRIVESERVICE from four information which I have i.e. CONSUMER_KEY, CONSUMER_SECRET, AuthToken, AuthSecret.
thanks.
Check the Google Drive SDK documentation for instructions on how to retrieve and use OAuth 2.0 credentials to instantiate a service object:
https://developers.google.com/drive/credentials
Please note that the Drive API is supported by the Google APIs Client Library for .NET:
http://code.google.com/p/google-api-dotnet-client/

Get logged on user's SMTP address via EWS?

I have a client app written using EWS Managed API 1.1. Here's the situation:
The client does not run on a computer within the same domain as the Exchange Server.
I have the username and password of a user, but not their email address.
There's no commonality between username (e.g. ABC123\001234) and email address (e.g. joe.bloggs#company.com).
I can connect to EWS just fine, send messages, etc.
However my software needs to discover the authenticated user's email address, and for various requirements reasons can't just ask the user to provide it.
I assumed I'd be able to get such a simple detail back from the web service, but I'm stumped!
Is this possible for both 2007 and 2010?
Thanks!
You may be able to do it using ExchangeService.ResolveName. I tried it with the following EWS Managed API code example on Exchange 2007 and it worked like a charm:
var service = new ExchangeService(ExchangeVersion.Exchange2007_SP1);
service.Url = new Uri("https://serv/EWS/exchange.asmx");
service.Credentials = new NetworkCredential("001234", "PasswordForUser001234", "Domain");
ServicePointManager.ServerCertificateValidationCallback = (object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) =>
{
return true;
};
var resolvedNames = service.ResolveName("001234");
foreach (var resolvedName in resolvedNames)
{
Console.WriteLine(resolvedName.Mailbox.Address);
}