I am using Microsoft Identity's OAuth 2.0 support to send email using Microsoft Graph.
Created a personal email account as XXXX#outlook.com. Using this account I login to Azure AD and create a tenant there. Used ClientCredentialProvider (From msgraph-sdk-auth-java) as authorizer trying to send an email to myself.
Steps:
Created a Tenant account.
Created an application and given permission in Graph>Application->Send.email etc
Created a Secret key
Below is the error I am getting:
POST microsoft.graph.sendMail
SdkVersion : graph-java/v1.5.0 Authorization : Bearer
_xv1yPye...
{
"message": {
"subject": "Test",
"body": {
"contentType": "text",
"content": "The new cafeteria is open bujji."
},
"toRecipients": [
{
"emailAddress": {
"address": "xxxxx#outlook.com"
}
}
]
},
"saveToSentItems": true
}401: UnauthorizedStrict-Transport-Security: max-age=31536000Cache-Control: privatex-ms-ags-diagnostic: {
"ServerInfo": {
"DataCenter": "South India",
"Slice": "SliceC",
"Ring": "3",
"ScaleUnit": "001",
"RoleInstance": "AGSFE_IN_1"
}
}client-request-id: 01565263-11b4-45f7-b089-06f57fdd8241request-id: 2e0cac3b-dc32-4dab-bb30-769590fc156eContent-Length: 361Date: Tue,
16Jun202007: 14: 42GMTContent-Type: application/json{
"error": {
"code": "OrganizationFromTenantGuidNotFound",
"message": "The tenant for tenant guid \u002706841624-5828-4382-b0a0-XXXXXX87b08f\u0027 does not exist.",
"innerError": {
"requestId": "01565263-11b4-45f7-b089-06f57fdd8241",
"date": "2020-06-16T07:14:43",
"request-id": "2e0cac3b-dc32-4dab-bb30-769590fc156e"
}
}
}
private static void sendEmail() {
ClientCredentialProvider authProvider = new ClientCredentialProvider(
"fb7f0ecc-b498-XXXX-XXXX-b016f252ea7d",
Arrays.asList("https://graph.microsoft.com/.default"),
"8-rpF8sOwV.CWF~7gK.XXXXXXXX.SSScxj0",
"06841624-5828-4382-b0a0-XXXXXXe87b08f",
NationalCloud.Global);
IGraphServiceClient graphClient = GraphServiceClient.builder().authenticationProvider(authProvider).buildClient();
Message message = new Message();
message.subject = "Test";
Ite * mBody body = new ItemBody();
body.contentType = BodyType.TEXT;
body.content = "The new cafeteria is open.";
message.body = body;
LinkedList < Recipient > toRecipientsList = new LinkedList < Recipient > ();
Recipient toRecipients = new Recipient();
EmailAddress emailAddress = new EmailAddress();
emailAddress.address = "xxxxx#outlook.com";
toRecipients.emailAddress = emailAddress;
toRecipientsList.add(toRecipients);
message.toRecipients = toRecipientsList;
graphClient.me()
.sendMail(message, true)
.buildRequest()
.post();
}
I guess you want to use Microsoft Graph API to send email from your personal account email XXXX#outlook.com.
But when you use this account to login to Azure AD and create a tenant, and use ClientCredentialProvider in your code, the account will be treated as a work account (not personal account) of your tenant.
So when a work account wants to send an email, it will requires an Exchange online license of O365 subscription. You don't have O365 subscription with Exchange online license. That is why you get this error: The tenant for tenant guid \u002706841624-5828-4382-b0a0-XXXXXX87b08f\u0027 does not exist.
If you want to send email from your personal account, it's unnecessary to create an AAD tenant. And you should use Authorization code provider rather than Client credentials provider. Another thing is that personal account requires Delegated permission rather than Application permission based on Send mail permissions. Create an application and give permission in Graph > Delegated > Mail.Send.
Please note it may require the scopes as https://graph.microsoft.com/mail.send instead of https://graph.microsoft.com/.default.
Thanks, Allen for your help. I am able to send and receive emails from my outlook account. Using Authorization code provider
1. Login to Azure AD create an Application in "Application from Personl account".
2. Give permission Graph > Delegated > Mail.Send.
3. Provided Redirect URL as http://localhost:8080/muapp".Note Down all appId,Create a secret Key.
4.Now hit the below URL with the details
https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=40fcd457-1807-49e3-8bce-XXXXXX40ca194&response_type=code&redirect_uri=https://localhost/myapp/&response_mode=query&scope=openid%20offline_access%20https%3A%2F%2Fgraph.microsoft.com%2Fmail.send%20https%3A%2F%2Fgraph.microsoft.com%2Fmail.read&state=12345
5. Acquire the code.This code we need to pass in Authorization code provider.
6.Scope "https://graph.microsoft.com/mail.send"
7. Authority "https://login.microsoftonline.com/consumers"
I have one question every time send an email I have to Acquire the code. Is there any Way this will have expiry date etc.???
Related
I can create a ProjectApiRoot using the Java SDK and perform requests with that using the following code:
private static ProjectApiRoot createProjectClient() {
ProjectApiRoot apiRoot = ApiRootBuilder.of()
.defaultClient(ClientCredentials.of()
.withClientId(System.getenv("CTP_CLIENT_ID"))
.withClientSecret(System.getenv("CTP_CLIENT_SECRET"))
.build(),
ServiceRegion.GCP_EUROPE_WEST1)
.build(System.getenv("CTP_PROJECT_KEY"))
return apiRoot
}
However, I would like to authorize as a specific customer (email and password) and interact with the Commercetools API using the customer. The following code throws an error:
private static ProjectApiRoot createCustomerClient() {
def tokenUri = "https://auth.europe-west1.gcp.commercetools.com/oauth/*CTP_PROJECT_KEY*/customers/token"
def projectKey = System.getenv("CTP_PROJECT_KEY")
def scopes = System.getenv("CTP_SCOPES")
def credentials = ClientCredentials.of()
.withClientId("*email*")
.withClientSecret("*password*")
.withScopes(scopes)
.build()
def apiRootBuilder = ApiRootBuilder.of()
.withApiBaseUrl("https://api.europe-west1.gcp.commercetools.com")
.withClientCredentialsFlow(credentials, tokenUri)
return apiRootBuilder.build(projectKey)
}
Error:
io.vrap.rmf.base.client.oauth2.AuthException: detailMessage: Unauthorized
"message" : "Please provide valid client credentials using HTTP Basic Authentication.",
By using the withGlobalCustomerPasswordFlow instead of the withClientCredentialsFlow which authenticates the customer prior to doing the request.
But I would advise to do this only in a context where the customer is logging in everytime. Using it in any other context e.g. remembered log in of needs a more sophisticated approach as you need to store the bearer token and refresh token and can't easily use the middleware approach for authenticating the customer but instead do it not as part of an auth flow middleware.
Please see also https://github.com/commercetools/commercetools-sdk-java-v2/tree/main/commercetools/commercetools-sdk-java-api/src/integrationTest/java/commercetools/me
Do you guys use the Google Cloud Platform, specifically the APP ENGINE ENDPOINTs for professional/production purposes? Asking because I am not comfortable with response time. It´s taking too long to perform simple http POST requests like registering an users with only e-mail and password (saving in a DATASTORE). In this URL you will find the register form, let me know what you guys thing about the response time, is it acceptable?
https://filiperebollo1986.appspot.com/register.html
My Java code is quite simple. Only instantiate an user object with email and password which does´t have any special encrypting algorithm. Naturally for the registering purpose I need to check if the user already exists and after that save the object in the DATASTORE. I can ensure that the problem is not in my front-end apps because takes too long in the iOS e Android apps too.
POST:
https://filiperebollo1986.appspot.com/_ah/api/igardenendpoints/v12/saveProfile.
{
"userEmail": "response_time",
"password": "123"
}
Response:
200
Show headers
{
"messageText": "User successfully created!",
"messageNumer": 0,
"kind": "igardenendpoints#resourcesItem",
"etag": "\"9e-bXvBAIkMzDxg9PvVcUBMIXB0/_RzWmXdehTtOTMg1Y7MhIvXy5-k\""
}
public Message saveProfile(ProfileForm profileForm) {
Message message;
String userEmail = profileForm.getUserEmail();
String password = profileForm.getPassword();
String displayName = profileForm.getDisplayName();
Profile profile = ofy().load().key(Key.create(Profile.class, profileForm.getUserEmail())).now();
if (profile == null) {
if (displayName == "") {
displayName = extractDefaultDisplayNameFromEmail(profileForm.getUserEmail());
}
profile = new Profile(userEmail, password, displayName);
message = new Message("User successfully created!", 0);
} else {
message = new Message("User already exists!", 1);
}
ofy().save().entity(profile).now();
return message;
}
I am creating an android application with an app engine back end. I used the google app engine plugin to create an android connected app. Here is my entity.
#PersistenceCapable(identityType = IdentityType.APPLICATION)
public class User {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
Long id;
#Persistent
String email;
#Persistent
String password;
I used the plugin to generate my endpoint. To test it, I run the app engine as a web application and I can use every method successfully. I navigate to
http://localhost:8888/_ah/admin/datastore
and I can see the records that I entered. To be sure the generated getUser method works I go to the API explorer and retrieve user with id = 1 and I get
{
"id": "1",
"email": "this#that.com",
"password": "1234"
}
So I know it works. In my android app I connect to the endpoint like this:
User user = new User();
Userendpoint.Builder endpointBuilder = new Userendpoint.Builder(AndroidHttp.newCompatibleTransport(), new JacksonFactory(), new HttpRequestInitializer() {
public void initialize(HttpRequest httpRequest) {
}
});
Userendpoint endpoint = CloudEndpointUtils.updateBuilder(endpointBuilder).build();
try {
User result = endpoint.getUser(1L).execute();
} catch (IOException e) {
e.printStackTrace();
}
And I get the following error
com.google.api.client.googleapis.json.GoogleJsonResponseException: 503 Service Unavailable
{
"code" : 503,
"errors" : [ {
"domain" : "global",
"message" : "javax.jdo.JDOObjectNotFoundException: Could not retrieve entity of kind User with key User(1)\nNestedThrowables:\norg.datanucleus.exceptions.NucleusObjectNotFoundException: Could not retrieve entity of kind User with key User(1)",
"reason" : "backendError"
} ],
Any advice or ideas would be greatly appreciated!
The error was that I was connecting to the development server from my laptop, but I was connecting to the production server on my device.
Change
// For development server
protected static final String LOCAL_APP_ENGINE_SERVER_URL_FOR_ANDROID = "http://<Your IP>:<your port>/_ah/api";
// For production server
protected static final String LOCAL_APP_ENGINE_SERVER_URL_FOR_ANDROID = "https://<your version>-<your project ID>.appspot.com/_ah/api/";
I'm trying to retrive Calendar events for a user in a domain. I have service account access, but i get 404 error when I try to get specific user events.
Heres connection code:
NetHttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
GoogleCredential credential = new GoogleCredential.Builder()
.setTransport(httpTransport)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId(googleApiServiceAccountId)
.setServiceAccountScopes(Collections.singleton(CalendarScopes.CALENDAR_READONLY))
.setServiceAccountPrivateKey(SecurityUtils.loadPrivateKeyFromKeyStore(
SecurityUtils.getPkcs12KeyStore(),
getClass().getClassLoader().getResourceAsStream(googleApiPrivateKeyPath),
NOTASECRET, PRIVATEKEY, NOTASECRET))
.build();
calendarApi = new Calendar.Builder(httpTransport,
JSON_FACTORY, credential).setApplicationName(getApplicactionName()).build();
Events listing method:
public List<Event> getCalendarEventsForUserAndDates(String userEmail, Long dateFrom, Long dateTo) {
try {
String pageToken = null;
List<Event> allEvents = Lists.newArrayList();
do {
ArrayMap<String, Object> parameters = new ArrayMap<String, Object>();
parameters.add("xoauth_requestor_id", userEmail);
Calendar.Events.List list = calendarApiBean.getCalendarApi()
.events().list("primary");
list.setTimeMax(new DateTime(dateFrom, 0))
.setTimeMin(new DateTime(dateTo, 0))
.setUnknownKeys(parameters);
Events events = list.setPageToken(pageToken)
.execute();
List<? extends Event> items = events.getItems();
if (items != null) {
allEvents.addAll(items);
}
pageToken = events.getNextPageToken();
} while (pageToken != null);
return allEvents;
} catch (IOException e) {
logger.error("error while retriving calendar events for {} and dates {} {} ", userEmail, dateFrom, dateTo);
logger.error("exception", e);
return Collections.emptyList();
}
}
When i try to set xoauth_requestor_id to user's email and list 'primary', i get Calendar events for my Service Accounts. When I change events().list() parameter to user's email I get an following Error:
com.google.api.client.googleapis.json.GoogleJsonResponseException: 404 Not Found
{
"code" : 404,
"errors" : [ {
"domain" : "global",
"message" : "Not Found",
"reason" : "notFound"
} ],
"message" : "Not Found"
}
Thanks for any help.
To solved this problem I had to add this service account to authorized API clients on Domain Administration.
Client name is Service Account Client ID and scope was for Readonly Calendar
This consists of two different parts
Creating a service account
Giving it permissions to access the scopes you need
Create the service account:
Go to the cloud page to administer your Service Accounts:
Menu on the left side -> IAM & admin -> Service accounts. Or click on this link: https://console.cloud.google.com/iam-admin/serviceaccounts
Make sure you are logged in as the user you want to use, and check that the organization is correctly selected
Create a new service account with Project Viewer role (you can change this later), and download the .json file with the credentials when you click to create a key.
Edit the service account and enable G Suite Domain-wide Delegation
Take note of the Client ID. (NOT the Key ID)
Give it permissions:
Go to admin.google.com
Make sure you are logged in as the user with admin rights.
Then go to Security -> Settings -> Advanced settings -> Manage API client access
In client name fill in the Client ID you copied above (NOT the Key id)
Fill in the scopes you need and authorize the service account. ie:
https://www.googleapis.com/auth/calendar.readonly,https://www.googleapis.com/auth/gmail.readonly
I tested the Python samples I link in the sources, and can confirm this works as expected.
Sources:
https://developers.google.com/identity/protocols/OAuth2ServiceAccount
https://support.google.com/a/answer/162106?hl=en
I am trying to write to Cloud Storage with the REST API using this code:
public static void insertData() {
try {
StorageObject st = new StorageObject();
//create the media object
Media m = new Media();
String content = "hi! this is a test";
m.setData(Base64.encodeBase64String(content.getBytes()));
m.setContentType("text/html");
st.setMedia(m);
//this gets me the credential, works for other APIs but not cloud storage
Storage storage = RequestBuilder.buildStorage();
//Create the insert and execute
Insert insert = storage.objects().insert("mybucket", st);
insert.execute();
} catch (IOException e) {
log.severe(e.getMessage());
}
}
This is my ACL entry as per the REST API:
"kind": "storage#bucketAccessControls",
"items": [
{
"kind": "storage#bucketAccessControl",
"id": "gammeprediction/allUsers",
"selfLink": "https://www.googleapis.com/storage/v1beta1/b/gammeprediction/acl/allUsers",
"bucket": "mybucket",
"entity": "allUsers",
"role": "OWNER"
}]
This is how I get the credential:
private static Credential authorize() {
GoogleCredential credential = null;
//load properties
Properties appProperties = new Properties();
appProperties.load(RequestBuilder.class
.getResourceAsStream("/app.properties"));
// creates an authorization with the key and service account given
InputStream is = RequestBuilder.class.getResourceAsStream("/"
+ appProperties.getProperty("app.keyFileName"));
PrivateKey pk;
try {
pk = PrivateKeys.loadFromKeyStore(KeyStore.getInstance("PKCS12"),
is, "notasecret", "privatekey", "notasecret");
credential = new GoogleCredential.Builder()
.setTransport(HTTP_TRANSPORT)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId(
appProperties
.getProperty("app.serviceAccount"))
.setServiceAccountPrivateKey(pk)
.setServiceAccountScopes(PredictionScopes.PREDICTION,
DriveScopes.DRIVE, StorageScopes.DEVSTORAGE_FULL_CONTROL).build();
return credential;
}
The permissions on the bucket are OWNER for allUsers, but I still get a 403 Forbidden "Access not configured" error. What could possibly be wrong?
Once the JSON API is generally available, this logic will work.
However, at the moment, the JSON API is in Limited Preview. Since an unknown user is not considered to be a member of the limited preview, completely anonymous queries via the REST API are currently not possible. Instead, you must provide at a bare minimum a whitelisted API key when you connect. If you provide no further identity information, you'll be treated as an anonymous user. Or, going further, you can use OAuth2 credentials instead to be treated as a registered user. For more, see: https://developers.google.com/storage/docs/json_api/
Is that a GWT RequestBuilder? I'm not entirely familiar with its use, unfortunately. If it helps, here's an example of setting up a connection with an API key using the Google API Java Client: https://code.google.com/p/google-api-java-client/wiki/OAuth2#Unauthenticated_access
Also, it looks like your call to setData() is passing a non-base64'd string, which will fail.