Gmail API userId use cases in Gmail NodeJs client? - node.js

How do I use the userId property outside the cases where I would use the special value 'me'?
From what I understand, when you use 'me', is when you've authenticated as the end user.
But when you're not authenticated as the end user, you can't use 'me' nor can you use any other value because you don't have permissions to perform a given request.
I'm trying to understand how can I perform an action on behalf of an authenticated user on my app without using the value 'me' in the userId field?
Example using Gmail nodeJs client:
const googleClient = new google.auth.OAuth2(client_id, client_secret, redirect_uri);
async function getUnreadEmailsFrom(userID) {
let gmail = google.gmail({ version: 'v1', auth: googleClient });
/* Here on the line below, I'd like to use the email or ID of an already authenticated user in order to perform a given action.
But the only way I understand that I am able to perform any action is by loading the token of the user and saving it via
googleClient.setCredentials(userToken)
and then using the special value 'me' inside the userId propery.
*/
let response = await gmail.users.messages.list({userId: 'me', q: 'is:unread', maxResults: 500 });
return response.data;
}

Short answer:
The only allowed values for userId are me and the authenticated (or impersonated) user email address.
Explanation:
The only way to access other users' data is to grant domain-wide authority to a service account and use that to impersonate the target user whose data you want to access (this user should be in the same domain).
Nevertheless, the target user email is specified when impersonating it (see Preparing to make an authorized API call), that is, before calling the API.
Later, when calling the API, the special value me would correspond to the impersonated user, and the only accepted values would be me and the impersonated user email. Any other value would result in error (since the service account would be acting as the impersonated user, and would only be able to access the impersonated user's data).
Therefore, the only alternative to me is the authenticated user email address, but of course you'd get the same data.
Domain-wide delegation example:
creds = service_account.Credentials.from_service_account_file('credentials.json', scopes=SCOPES)
creds = creds.with_subject('your_target_user#email.com')
service = build('gmail', 'v1', credentials=creds)
messages = service.users().messages().list(userId="me").execute()

Related

How to get user details from an Azure AD token?

Using this tutorial as a guide: https://github.com/Azure-Samples/ms-identity-javascript-angular-spa-aspnetcore-webapi
In the web api we need to obtain the users details (for audit purposes we want to record the details of the user in the database).
In the web api, is there a way to decode the azure token to obtain the user details? Or we could simply pass the user email from the client browser in each request also be feasible (this is easily accessible using the MSAL library for Angular)
Rather than trying to decode the token to get the current users email, the base controller exposes the current user as detailed here;
https://learn.microsoft.com/en-us/aspnet/core/migration/claimsprincipal-current?view=aspnetcore-2.1
So simply using the following I can obtain the user associated with the current request;
string email = User.FindFirstValue(ClaimTypes.Email);
You could decode token to get some information of the signed-in user such as username and email, try token with https://jwt.io/.
If you want more details, you can use MS Graph. Me means current signed-in user. Try with following Graph SDK or HttpClient sample.
GraphServiceClient graphClient = new GraphServiceClient( authProvider );
// or graphClient.Users[user-object-id].Request().GetAsync(); for a specific user
var user = await graphClient.Me
.Request()
.GetAsync();

Background Access to microsoft Graph API

i'm developing a single page application for a costumer. I need to create a script that allows me to access to my app without user interaction. for example i press a button and the script automatically log me in (i know username and password of the user). The user don't need to see the window where i put username and password.
Is it possible? at the moment my login script is:
const authResult = await msalClient.loginPopup(msalRequest);
localStorage.setItem('msalAccount', authResult.account.username);
// Get the user's profile from Graph
user = await getUser();
// Save the profile in session
localStorage.setItem('graphUser', JSON.stringify(user));
if(accountAttivo!=""){
setActiveUser();
}
updatePage(Views.home);
Looks like you are trying to follow the ROPC flow as you are using username and password credentials in your script, access token must be fetched from AAD before we call getUser(). To fetch any details using graph api we need to have access token please go through the article which helps you more in understanding.
Let's say we wrote a function called getAccessToken() to fetch the token. Once the token fetched and is saved in the sessions, following code can be used in getUser() to fetch the user profile.
const options = {
authProvider,
};
const client = Client.init(options);
let res = await client.api('/me')
.get();
Calling both the functions getAccessToken() and getUser() in the code flow of the button click event should bypass the user interaction with the application to enter credentials.
NOTE: Microsoft does not recommend to user ROPC flow. This most scenarios, more secure alternatives are available and recommended.
This flow requires a very high degree of trust in application, and
carries risks which are not present in other flows. You should only
use this flow when other more secure flows can’t be used.

passing properties in callback function nodejs

I'm using paypal-rest-sdk. Problem I'm facing is, when I'm making an authorizationUrl call, I want to pass some parameters which can be accessed in the redirected URL.
Below is my code
import paypal from 'paypal-rest-sdk';
const openIdConnect = paypal.openIdConnect;
paypal.configure({
mode: "sandbox"
client_id: //MyClientId,
client_secret: //MySecretId,
openid_redirect_uri: `http://myRedirectionEndpoint/account/domestic/paypal/callback?state={accountId:5e8c2291d69ed1407ec86221}`
});
openIdConnect.authorizeUrl({scope: 'openid profile'});
Adding query parameter state gives the error as invalid redirectUri
What is the best way to pass the data that needs to be used after redirection
I think you are slightly misunderstanding how oauth authorization works. Basically if you want to get any data you need to do this AFTER you consume the callback and validate the user in your system as well.
Have you ever seen for Google/github etc openid auth provider returning some data that corresponds to the caller system's data? It's not possible.
You are probably confusing this with webhook where the caller system calls a webhook with some data internally and you capture it. Which is commonly used in payment transactions.
But the auth is slightly different. For auth there are 3 systems.
the actual auth provider (Paypal/google/github) etc.
an Identity provider which basically gets profile data etc and other than for enterprise systems these two systems are simply same.
the caller system which is your NodeJS service in this case.
=> Now caller-system calls the auth provider to get some kind of code generally an auth code. This means the user exists in auth system let's say Google.
=> Then the caller-system calls the identity provider with that auth code checking if the user is there in identity provider(idp) as well and the idp returns access_token, id_token, refresh_token etc (as I said most of the time these are same systems). But consider amazon, let's say you want to login to Amazon with your Google account. You have a Google account alright but you don't have amazon account. So you will get the auth code but will not get the id_token.
=> Now the id_token most of the time contains some basic info of the user in JWT format. But Now the ACCESS_TOKEN is used to do all the other calls to your system(caller system). Now as I said id_token some kind of user data. You can have a db table mapping userid with account number in your NodeJs service.
=> Make an endpoint to get the account number or something which takes access_token and id_token. First validate the access_token and verify the signature of the id_token then decrypt the token to get basic user info. and use that id to fetch the data from your table and use that data.
After Edit:
You can see in the doc:
paypal.configure({
'openid_client_id': 'CLIENT_ID',
'openid_client_secret': 'CLIENT_SECRET',
'openid_redirect_uri': 'http://example.com' });
// Authorize url
paypal.openIdConnect.authorizeUrl({'scope': 'openid profile'});
// Get tokeninfo with Authorize code
paypal.openIdConnect.tokeninfo.create("Replace with authorize code", function(error, tokeninfo){
console.log(tokeninfo);
});
// Get userinfo with Access code
paypal.openIdConnect.userinfo.get("Replace with access_code", function(error, userinfo){
console.log(userinfo);
});
When you get the auth code, you use it to call the paypal.openIdConnect.tokeninfo.create and get the tokens. Then use those tokens to call the paypal.openIdConnect.userinfo.get to get the user Info. Now when you get the userinfo you will be able to create the db row that you wanted to create.
You can add those two below calls in your /callback route.

Azure App Service Easy Auth

I have an Azure mobile backend set up with easy auth for facebook and google authentication and it works as expected.
Every time a user signs in with any of the supported providers, I want to be able to verify if it's a new user or not (e-mail not in database), without make an additional call from client. Is this possible?
Every time a user signs in with any of the supported providers, I want to be able to verify if it's a new user or not (e-mail not in database), without make an additional call from client. Is this possible?
As far as I know, we couldn't directly verify if it's a new user or not.
No matter you use server flow or client flow, easy auth will just return access token for the client to access the mobile backend resources, it will not check the user is new or old.
If you want to achieve this requirement, you need write your own logic.
You could write codes after the user login successfully.
For example, facebook login.
If you the use have login successfully,you could call GetAppServiceIdentityAsync extension method to get the login credentials, which include the access token needed to make requests against the Facebook Graph API.
// Get the credentials for the logged-in user.
var credentials =
await this.User
.GetAppServiceIdentityAsync<FacebookCredentials>(this.Request);
if (credentials.Provider == "Facebook")
{
// Create a query string with the Facebook access token.
var fbRequestUrl = "https://graph.facebook.com/me/feed?access_token="
+ credentials.AccessToken;
// Create an HttpClient request.
var client = new System.Net.Http.HttpClient();
// Request the current user info from Facebook.
var resp = await client.GetAsync(fbRequestUrl);
resp.EnsureSuccessStatusCode();
// Do something here with the Facebook user information.
var fbInfo = await resp.Content.ReadAsStringAsync();
}
Then you could check the database according to the user information.
More details about how to get user information in server side, you could refer to How to: Retrieve authenticated user information.

CRON Node.js Gmail API script

How do I properly setup Gmail API script that sends emails?
I am about to use this method and I started building my script from this quickstart guide.
Is there alternative way to do this without using OAuth 2 validation? Or a way to validate once for all?
Well, in using Gmail APi with your app, you need to use OAuth 2.0 because all request to the Gmail API must be authorized by an authenticated user. And if you notice the quickstart, there is a step here that you need to create a credentials/Outh client ID to make this API work.
For more information, there is another way to authorize your app with Gmail. You can do it with the help of Google+ Sign-in that provide a "sign-in with Google" authentication method for your app.
While asking for authorization from GMail, OAuth 2.0 gives one access token and one refresh token. To avoid validation every time, store the access token. Use the refresh token to get the new access token after it is expired (access token expires every one hour).
Read about this process here: https://developers.google.com/identity/protocols/OAuth2
I found solution using JWT to authorize OAuth2.
You need to have admin account to create Domain wide delegation service account. Then in Developer console you need to download service key JSON file which you load as credentials.
First fetch all users like this: (here you need to use account with admin directory rights)
const google = require('googleapis');
const gmail = google.gmail('v1');
const directory = google.admin('directory_v1');
const scopes = [
'https://www.googleapis.com/auth/gmail.readonly',
'https://www.googleapis.com/auth/admin.directory.user.readonly'
];
const key = require('./service_key.json');
var authClient = new google.auth.JWT(
key.client_email,
key,
key.private_key,
scopes,
"authorized#mail.com"
);
authClient.authorize(function(err, tokens){
if (err) {
console.log(err);
return;
}
directory.users.list(
{
auth: authClient,
customer: 'my_customer',
maxResults: 250,
orderBy: 'email'
}, (err, resp) => {
if (err) {
console.log(err);
return;
}
console.log(resp);
});
});
Then you need to fetch Thread lists (100 per request (page)). And for each thread object you need to call get method for full thread. When using Gmail API authorize as user you want to fetch emails from. In request as userId use value 'me'.

Resources