I've been working on an application that uses the Gmail API, but I've kind of hit a roadblock.
I need user Send-As information, but it doesn't appear to be available in the Gmail API. In Google Apps Accounts, the EmailSettings API provides this functionality, but it is unavailable for free gmail accounts.
I suppose my question is: Is there any way to pull a list of Send-As or POP-configured accounts from a regular Gmail account?
Sadly, you can not retrieve them from the Gmail API or any other API I know of. Other 3rd party applications that use the feature of "aliasing" their mail advise their users to first create the alias in the regular Gmail application first, and then input it manually.
You could do that, and in the background send a mail to the user's own inbox with the given alias. If the alias exists, it will be the sender of the mail. If it was incorrect, it will fall back to the regular email address.
Edit
You can now get the sendAs settings from the Gmail API:
Request
GET https://www.googleapis.com/gmail/v1/users/me/settings/sendAs?access_token={access_token}
Response
{
"sendAs": [
{
"sendAsEmail": "primary#gmail.com",
"displayName": "",
"replyToAddress": "",
"signature": "",
"isPrimary": true,
"isDefault": true
},
{
"sendAsEmail": "secondary#gmail.com",
"displayName": "",
"replyToAddress": "",
"signature": "",
"isDefault": false,
"treatAsAlias": true,
"smtpMsa": {
"host": "host.com",
"port": 465,
"securityMode": "ssl"
},
"verificationStatus": "accepted"
}
]
}
Related
What is the recommended architecture using Microsoft Azure AD to implement delayed mail sending on behalf of a user in a React frontend and Python backend?
I want the user to connect to their Microsoft account in the frontend and obtain the necessary tokens in my backend for subsequent use in sending emails on behalf of the user.
Should I use OAuth 2.0 or OpenID Connect for the user authentication in my frontend?
I am currently using MSAL in my frontend to allow the user to connect to their Microsoft account, but I am unable to obtain a refresh token in my backend. I have set my frontend as an SPA and my backend as a web application in Azure AD, but I am not sure if this is the correct configuration. Are there any best practices or recommended approaches to achieve this?
Progress shareing:
prog 1:
I obtained an authorization code in my frontend by following the instructions on this page:
https://learn.microsoft.com/en-us/azure/energy-data-services/how-to-generate-refresh-token#get-authorization.
I then sent this authorization code to my backend to obtain a refresh token, but I am encountering an error that says 'AADSTS9002313: Invalid request. Request is malformed or invalid.'
I suspect that the error is caused by the fact that I'm using different uri's in my frontend and backend. If this is the case, how can I pass the information needed to get the refresh token to my backend?
[Both endpoints included in web urls in Azure AD]
prog 2:
I have obtained an access_token using this method, but I am unable to obtain a refresh token. While the "client_credentials" grant_type works, the "authorization_code" grant_type is not working for me. I am unsure whether the issue lies with Azure AD or if I am not sending the correct body parameters.
prog 3:
Successfully obtain both an access token and a refresh token.
prog 4:
failed to send mail:
"{'error': {'code': 'ErrorAccessDenied', 'message': 'Access is denied. Check credentials and try again.'}}"
prog 5:
Email sent but the sender is user-id#outlook.com instead of user-email#domain.xx
Note that: If you want send mail on behalf of a user client_credentials flow. Use Authorization code flow for user interaction.
I tried to reproduce the same in my environment and got the results like below:
I created an Azure AD Application and added Application permission:
I generated access token using Client Credentials by using below parameters:
https://login.microsoftonline.com/TenantID/oauth2/v2.0/token
client_id:ClientID
client_secret:ClientSecret
scope:https://graph.microsoft.com/.default
grant_type:client_credentials
To send mail, I used below query:
POST https://graph.microsoft.com/v1.0/users/FromAddress/sendMail
{
"message": {
"subject": "hi",
"body": {
"contentType": "Text",
"content": "how are you"
},
"toRecipients": [
{
"emailAddress": {
"address": "****"
}
}
],
"ccRecipients": [
{
"emailAddress": {
"address": "****"
}
]
},
"saveToSentItems": "false"
}
As mentioned by you, using Authorization code is not working.
I created an Azure AD Application and added Delegated permissions:
I generated auth-code using below endpoint:
https://login.microsoftonline.com/TenantID/oauth2/v2.0/authorize?
&client_id=ClientID
&response_type=code
&redirect_uri=https://jwt.ms
&response_mode=query
&scope=mail.send offline_access
&state=12345
Now, I generated access token using below parameters:
https://login.microsoftonline.com/TenantID/oauth2/v2.0/token
grant_type:authorization_code
client_id:ClientID
scope:mail.send offline_access
code:code
redirect_uri:https://jwt.ms
client_secret:ClientSecret
To send mail, I used below query:
POST https://graph.microsoft.com/v1.0/me/sendMail
{
"message": {
"subject": "hi",
"body": {
"contentType": "Text",
"content": "how are you"
},
"toRecipients": [
{
"emailAddress": {
"address": "****"
}
}
],
"ccRecipients": [
{
"emailAddress": {
"address": "****"
}
]
},
"saveToSentItems": "false"
}
Reference:
user: sendMail - Microsoft Graph v1.0 | Microsoft Learn
When creating multi-tenant apps that will use Azure AD Authentication to sign users in, various samples on GitHub seem to suggest that the App Registration should include the following permissions clubbed under OpenId.
email View users' email address
offline_access Maintain access to data you have given it access to
openid Sign users in
profile View users' basic profile
(See for example: https://github.com/OfficeDev/microsoft-teams-sample-auth-node Section 12)
While following the samples on the Azure portal itself, the quickstarts create an App registration with just this one permission
User.Read Sign in and read user profile
Is Microsoft's Azure AD User.Read permission a superset of the generic openid's email, openid and profile permissions?
When creating a consent URL in the https://login.microsoftonline.com/common/adminconsent?client_id={client-id} form, only the User.Read seems to appear.
What is the recommended set of permissions that are recommended for a basic app that needs to sign users in?
I just spent quite a while testing this as part of my own learning today, and here's what I found.
tl;dr
The openid scope will get you an id_token, and an access_token that allows you to call the UserInfo endpoint (https://graph.microsoft.com/oidc/userinfo). If you want to call any of the Graph APIs, (other than UserInfo) then you'll want (at least) the User.Read scope.
The Details
I have a test AzureAD tenant in which I created a brand-new client app registration (with a SPA endpoint) and a brand-new user, thus ensuring there was no residual 'consent' anywhere.
I then used the browser to sign-in with a hand-crafted URL to request only the openid scope. With line breaks added and some chars redacted this was:
https://login.microsoftonline.com/a8257b21-...6263/oauth2/v2.0/authorize?
client_id=b3f87624-...e5b&
response_type=code&
redirect_uri=https://localhost&
response_mode=query&
scope=openid&
state=12345&
code_challenge=LrTIpxRwK...
code_challenge_method=S256&
prompt=login
For this I got the below consent prompt:
It's interesting to note that it asked to "View your basic profile" and "Maintain access to data..." when I didn't ask for profile or offline_access scopes.
I extracted the authorization code from the response and sent it to the https://login.microsoftonline.com/{{tenant_id}}/oauth2/v2.0/token endpoint with the necessary fields.
The id_token I got back didn't include claims the profile scope would imply, showing only the below
{
"aud": "b3f87624-...2fb5367e5b",
"iss": "https://login.microsoftonline.com/a8257b21...263/v2.0",
"iat": 1642367777,
"nbf": 1642367777,
"exp": 1642371677,
"rh": "0.AUYAIXs...L7U2fluAAB0.",
"sub": "tcK...WXUvzWqAc",
"tid": "a82...6263",
"uti": "SjLRuw...jlhAA",
"ver": "2.0"
}
However interestingly the access_token it returned is for the Graph audience (00000003-0000-0000-c000-000000000000) did list the scopes as including profile and email, and did include claims related to those, e.g. (with some omitted for clarity)
{
"aud": "00000003-0000-0000-c000-000000000000",
"iss": "https://sts.windows.net/a82...6263/",
"app_displayname": "TestApp",
"appid": "b3f87...67e5b",
"family_name": "Bull",
"given_name": "Pit",
"idtyp": "user",
"ipaddr": "67.183.2.129",
"name": "Pit Bull",
"oid": "08a5...673de",
"scp": "openid profile email",
"sub": "6JrD7...phCH7Y",
"tid": "a825...36263",
"unique_name": "pit#example.dev",
"upn": "pit#example.dev",
"ver": "1.0"
}
(Note: Obviously you shouldn't look in tokens that aren't for your audience, but it's all there in clear text, so?)
I did also get a refresh token back, which I saved for later.
With that access token I could call the UserInfo endpoint and also see the *name claims, which would indicate profile scope (I forget to set the email on my test user, else I'm guessing that would have shown too).
From https://graph.microsoft.com/oidc/userinfo
{
"sub": "tcK6D...UvzWqAc",
"name": "Pit Bull",
"family_name": "Bull",
"given_name": "Pit",
"picture": "https://graph.microsoft.com/v1.0/me/photo/$value"
}
However, with that same access token, if I tried to call any Graph APIs such as https://graph.microsoft.com/v1.0/me or https://graph.microsoft.com/v1.0/organization I was met with the following response.
{
"error": {
"code": "Authorization_RequestDenied",
"message": "Insufficient privileges to complete the operation.",
"innerError": {
"date": "2022-01-16T21:52:18",
"request-id": "e4f58...1611",
"client-request-id": "e4f5...a1611"
}
}
}
By running through the same flow again but this time with the scopes openid profile email, the only difference I could ascertain was that the id_token now included the below claims (and again, probably would have had the email address if I'd configured it on the account)
"name": "Pit Bull",
"oid": "08a5...73de",
"preferred_username": "pit#example.dev",
The access token looked the same, and the UserInfo result was the same (which makes sense if the access_token to auth to it looked the same). I'd also note I didn't get prompted for any additional consent, which would imply profile and email were implicitly added to my initial openid-only scope request.
At this point I took a break to feed the kids, and when I came back a couple hours later decided to try out the refresh token I received (without requesting offline_access scope). It worked fine and I got refreshed tokens, so this seems to be implicitly added also when just requesting openid.
For my next test I sent the same hand-crafted request to authenticate but included the User.Read scope. As expected, I got prompted for the additional consent:
It's interesting to note how it mentions "..and read basic company information". This aligns with the docs at https://learn.microsoft.com/en-us/graph/permissions-reference which state of User.Read: "With the User.Read permission, an app can also read the basic company information of the signed-in user for a work or school account through the organization resource.".
All the tokens I got back from this looked similar to before, but obviously with the addition of the User.Read scp in the access token. Now making requests to certain Graph APIs was met with success. For example:
https://graph.microsoft.com/v1.0/me or https://graph.microsoft.com/v1.0/users/pit#example.dev
{
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#users/$entity",
"businessPhones": [],
"displayName": "Pit Bull",
"givenName": "Pit",
"jobTitle": null,
"mail": null,
"mobilePhone": null,
"officeLocation": null,
"preferredLanguage": null,
"surname": "Bull",
"userPrincipalName": "pit#example.dev",
"id": "08a53...b673de"
}
https://graph.microsoft.com/v1.0/me/directReports (I didn't have any direct reports configured, but it was a successful query rather than access denied)
{
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#directoryObjects",
"value": []
}
And https://graph.microsoft.com/v1.0/organization (trimmed for readability - and again, I'd not set up org details for this account, hence the nulls)
{
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#organization",
"value": [
{
"id": "a8257...736263",
"businessPhones": [],
"city": null,
"country": null,
"postalCode": null,
"preferredLanguage": null,
"state": null,
"tenantType": "AAD",
"directorySizeQuota": {
"used": 39,
"total": 300000
},
"verifiedDomains": [
{
"capabilities": "Email, OfficeCommunicationsOnline",
"isDefault": false,
"isInitial": true,
"name": "example.onmicrosoft.com",
"type": "Managed"
},
{
"capabilities": "None",
"isDefault": true,
"isInitial": false,
"name": "example.dev",
"type": "Managed"
}
]
}
]
}
So there you have it. Add User.Read if you want to query anything from the Graph APIs, else just use openid (and optionally profile email) if you are happy just signing users in and using the id_token for your needs.
As a footnote to this I'd also recommend reading the couple paragraphs at https://learn.microsoft.com/en-us/graph/permissions-reference#remarks-15 . I was only testing with a v2 endpoint, but it does look like User.Read used to be required just to sign-in with the v1 endpoint, so it being included by default may be a residual effect of that. As it states of the V1 endpoint: "To successfully return an ID token, you must also make sure that the User.Read permission is configured when you register your app."
Hence the question if user.read subsumes open_id, email and profile
perms?
No, user.read does not contain them, they are independent permissions.
I have set these required perms but in the consent popup shown to the
Azure AD admin, email and profile and openid permissions do not show
up; only offlne_access and user.read shows.
email offline_access openid profile is usually the permissions of the OIDC protocol. There are some differences between oauth2.0 and openid connect. If you only need to log in as a user, you only need to use openid connect, and it will only return you the id token of the logged in user. Regarding why the email openid profile is not displayed on the admin consent page, I think this is a problem that is still being fixed, but I don’t think you need to worry about these because these permissions themselves are permissions that do not require the administrator’s consent. When you add them in API permissions, you can use them directly.
At this time, the offline_access ("Maintain access to data you have given it access to") and user.read ("Sign you in and read your profile") permissions are automatically included in the initial consent to an application. These permissions are generally required for proper app functionality - offline_access gives the app access to refresh tokens, critical for native and web apps, while user.read gives access to the sub claim, allowing the client or app to correctly identify the user over time and access rudimentary user information. please refer to this document
In an Azure Active Directory, I register an App and define application roles in the manifest as follows:
"appRoles": [
{
"allowedMemberTypes": [
"User"
],
"description": "Normal user access",
"displayName": "User",
"id": "a046aca0-a7c4-4c33-8377-e43c4d7bd89f",
"isEnabled": true,
"value": "User"
},
{
"allowedMemberTypes": [
"User"
],
"description": "Full admin access",
"displayName": "Admin",
"id": "0204f02c-d752-48b5-8b8f-2f8ccb7f83d7",
"isEnabled": true,
"value": "Admin"
}
],
"oauth2AllowIdTokenImplicitFlow": true,
"oauth2AllowImplicitFlow": true,
Whenever a user receives an access token, the roles are also included. Everything works fine.
Now we have created another Azure Active Directory. I registered an app and define app roles, exactly with same procedure as before. However the roles are not included in the access token. Could someone please help me to cope with this problem?
First please check if you have assigned the user to roles, you can refer to this tutorial.
Second, I'm not sure if you mean access token or id token. As far as I know, the role will not show in access token. It will just show in id token. You can use flow like authorization code grant or openID connect to sign a user in. The response will have a id_token.
Here is another post which is similar to this problem for your reference.
By the way, if we use "client_credentials" as grant type, we can see the permission roles(in appliction permissions) which added in API permissions(the access token contains a roles property). But if we use "password" grant type, the access token will not have this roles property. So please pay attention to the grant type which you used.
Hope it helps.
What we are planning to achieve is a role-based security for a Front end Angular-2 and back-end ASP.NET Web API application. We are doing the authentication process with the help of ADAL.js and storing the token in the local storage. We have also implemented the approach shown here i.e. to call the Graphi API and get the user groups to stuff them into Claims.
My question is : Is there anyway, we can add role claims from server to the bearerToken which is residing in the local storage. Or is there any better way to approach this issue.
The code sample mentioned assign the role based on the group. If you have the Azure AD basic version, it support to assign the role to the users/groups directly.
My question is : Is there anyway, we can add role claims from server to the bearerToken which is residing in the local storage. Or is there any better way to approach this issue.
Yes, it is possible. To issue the role claims we need to assign the users to assign the roles to users or group first. Then when the user acquire the token, the Azure AD would issue the relative role claims in the token.
You can refer the code sample for using the role claim from here.
And you may also be interest in groups claim developing.
ok i was struggling with this for a while and i have figured it out i believe.
First,
In Azure AD, set up your WebApi app as application type to be Web App / API.
go to Manifest file and add your roles like
[
{
"allowedMemberTypes": [
"User"
],
"displayName": "Reviewer",
"id": "0238c2bb-9857-4d07-b760-a47ec621d57a",
"isEnabled": true,
"description": "Reviewer only have the ability to view tasks and their statuses.",
"value": "reviewer"
},
{
"allowedMemberTypes": [
"User"
],
"displayName": "Approver",
"id": "000018cb-19e3-4f89-bf99-5d7acf30773b",
"isEnabled": true,
"description": "Approvers have the ability to change the status of tasks.",
"value": "approver"
}
]
Then create the the Client app as Application type to be Native app and add required permissions to the service you added above.
In the SPA Angular app add something like this
var endPoints = {
// "https://localhost:44386/" is the API URL
// "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx" is the Service Application ID
"https://localhost:44386/": "xxxxxxxxxxxxxxxxxxxxxxxxxx"
};
adalAuthenticationServiceProvider.init({
instance: "https://login.microsoftonline.com/",
// tenant is your tenant name (something like below)
tenant: "{NAME}.onmicrosoft.com",
// this is the Native app application ID (ClientID) you registered
clientId: "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyy",
extraQueryParameter: "nux=1",
endpoints: endPoints
}, $httpProvider);
}
]);
Then, in your startup.cs you need to set up the Service App like the following:
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
TokenValidationParameters = new TokenValidationParameters
{
/* "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx" is the Service Application ID. (Same as you registered in the client app above)*/
ValidAudience = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
RoleClaimType = "roles"
},
/*enant is your tenant name (same as you registered in client app above)*/
Tenant = "{NAME}.onmicrosoft.com"
});
Finally you need to go to Azure active directory => Enterprise application => all applications => select your webAPI service => Users and groups => then assign users to the roles.
When this is all done when you log through your client app to authenticate and call the webapi, adal.js and ada-angular.js will put the proper bearer token that contains roles
Good to learn this approach.
Ted, thanks for sharing your solution !
For those who are not familiar with operating Azure AD manifest file. The following is a good resource.
https://thinkthencode.wordpress.com/2016/04/24/azure-ad-using-app-roles-for-authorization/
"appRoles": [
{
"allowedMemberTypes": [
"User"
],
"displayName": "Reviewer",
"id": "0238c2bb-9857-4d07-b760-a47ec621d57a",
"isEnabled": true,
"description": "Reviewer only have the ability to view tasks and their statuses.",
"value": "reviewer"
},
{
"allowedMemberTypes": [
"User"
],
"displayName": "Approver",
"id": "000018cb-19e3-4f89-bf99-5d7acf30773b",
"isEnabled": true,
"description": "Approvers have the ability to change the status of tasks.",
"value": "approver"
}
]
I am building an events management web app for my clients based on StrongLoop API platform whereby I need to limit CRUD access to data to the currently logged in user (a client).
I have followed these tutorials https://github.com/strongloop/loopback-faq-user-management, https://github.com/strongloop/loopback-example-access-control to successfully login and logout, and now need to implement bringing back the correct data on the AngularJS client.
I have setup a relation on my 'events' model as follows:
"relations": {
"user": {
"type": "belongsTo",
"model": "User",
"foreignKey": "ownerId"
}
}
and also on the built-in User model:
"relations": {
"events": {
"type": "hasMany",
"model": "event",
"foreignKey": "ownerId"
}
}
Not sure where/how to define the access token after login to make API calls. Do I also need to apply a filter on $scope.events = Event.find(); to retrieve only the records where ownerID: <currentUserId> or should the ACLs achieve that for me?
Any help much appreciated.
1) Access token automatically saved to localStorage/sessionStorage and to angular-sdk internals. So it attached to authorization header on every request to API.
2) Yes, you should apply filter, because ACL just allow or deny access to remote methods. By the way, another way to query user's events is
User.events({id: currentUserId})