NodeJs API login B2C without B2C login page - javascript

All right?
I am in need of help with Azure AD B2C implementation. I need an API that manages the login for my application and that makes a connection with AAD B2C. However, I only find login options through the B2C login page. I would like to know if there is any way to implement in Node a form of login in my application without having to use the MS login screen. User entering with password and email and validating in AD. I saw that there is ROPC, but at the same time saying that it is an insecure method.
I am currently implementing it with ms graph, azure-graph, ms-rest-azure, but I think this way is wrong.
User enter the email prefix and password via post.
server.post('/login', (request, response) => {
const {email, password} = request.body
msRestAzure.loginWithUsernamePassword(`${email}#<tenant-name>.onmicrosoft.com`, `${password}`, { tokenAudience: 'graph', domain: tenantId }, function (err, credentials, subscriptions) {
if (err) console.log(err);
console.log(credentials.tokenCache)

Update: few days ago, they announced the preview feature for iframe compatibility (i.e. embedded login):
https://learn.microsoft.com/en-us/azure/active-directory-b2c/embedded-login?pivots=b2c-custom-policy
of course, it's better if you don't use it in production yet, but it might be the solution you were looking for. :)
Old answer:
The only way to achieve that is by using ROPC, as you mentioned. ROPC is insecure basically because for some time you have to manage the credentials of the user (unlike when the user directly inserts them in B2C), but in your case, this is a requirement.
You cannot achieve this with Graph API, because you'll be only able to register the user, update his/her data (incl. password), but you won't be able to get an access token.

Related

Single flow: sign user in via Google oAuth AND grant offline/server access?

I'm trying to implement Google sign-in and API access for a web app with a Node.js back end. Google's docs provide two options using a combo of platform.js client-side and google-auth-library server-side:
Google Sign-In with back-end auth, via which users can log into my app using their Google account. (auth2.signIn() on the client and verifyIdToken() on the server.)
Google Sign-in for server-side apps, via which I can authorize the server to connect to Google directly on behalf of my users. (auth2.grantOfflineAccess() on the client, which returns a code I can pass to getToken() on the server.)
I need both: I want to authenticate users via Google sign-in; and, I want to set up server auth so it can also work on behalf of the user.
I can't figure out how to do this with a single authentication flow. The closest I can get is to do the two in sequence: authenticate the user first with signIn(), and then (as needed), do a second pass via grantOfflineAccess(). This is problematic:
The user now has to go through two authentications back to back, which is awkward and makes it look like there's something broken with my app.
In order to avoid running afoul of popup blockers, I can't give them those two flows on top of each other; I have to do the first authentication, then supply a button to start the second authentication. This is super-awkward because now I have to explain why the first one wasn't enough.
Ideally there's some variant of signIn() that adds the offline access into the initial authentication flow and returns the code along with the usual tokens, but I'm not seeing anything. Help?
(Edit: Some advice I received elsewhere is to implement only flow #2, then use a secure cookie store some sort of user identifier that I check against the user account with each request. I can see that this would work functionally, but it basically means I'm rolling my own login system, which would seem to increase the chance I introduce bugs in a critical system.)
To add an API to an existing Google Sign-In integration the best option is to implement incremental authorization. For this, you need to use both google-auth-library and googleapis, so that users can have this workflow:
Authenticate with Google Sign-In.
Authorize your application to use their information to integrate it with a Google API. For instance, Google Calendar. 
For this, your client-side JavaScript for authentication might require some changes to request
offline access:
$('#signinButton').click(function() {
auth2.grantOfflineAccess().then(signInCallback);
});
In the response, you will have a JSON object with an authorization code:
{"code":"4/yU4cQZTMnnMtetyFcIWNItG32eKxxxgXXX-Z4yyJJJo.4qHskT-UtugceFc0ZRONyF4z7U4UmAI"}
After this, you can use the one-time code to exchange it for an access token and refresh token.
Here are some workflow details:
The code is your one-time code that your server can exchange for its own access token and refresh token. You can only obtain a refresh token after the user has been presented an authorization dialog requesting offline access. If you've specified the select-account prompt in the OfflineAccessOptions [...], you must store the refresh token that you retrieve for later use because subsequent exchanges will return null for the refresh token
Therefore, you should use google-auth-library to complete this workflow in the back-end. For this,
you'll use the authentication code to get a refresh token. However, as this is an offline workflow,
you also need to verify the integrity of the provided code as the documentation explains:
If you use Google Sign-In with an app or site that communicates with a backend server, you might need to identify the currently signed-in user on the server. To do so securely, after a user successfully signs in, send the user's ID token to your server using HTTPS. Then, on the server, verify the integrity of the ID token and use the user information contained in the token
The final function to get the refresh token that you should persist in your database might look like
this:
const { OAuth2Client } = require('google-auth-library');
/**
* Create a new OAuth2Client, and go through the OAuth2 content
* workflow. Return the refresh token.
*/
function getRefreshToken(code, scope) {
return new Promise((resolve, reject) => {
// Create an oAuth client to authorize the API call. Secrets should be
// downloaded from the Google Developers Console.
const oAuth2Client = new OAuth2Client(
YOUR_CLIENT_ID,
YOUR_CLIENT_SECRET,
YOUR_REDIRECT_URL
);
// Generate the url that will be used for the consent dialog.
await oAuth2Client.generateAuthUrl({
access_type: 'offline',
scope,
});
// Verify the integrity of the idToken through the authentication
// code and use the user information contained in the token
const { tokens } = await client.getToken(code);
const ticket = await client.verifyIdToken({
idToken: tokens.id_token!,
audience: keys.web.client_secret,
});
idInfo = ticket.getPayload();
return tokens.refresh_token;
})
}
At this point, we've refactored the authentication workflow to support Google APIs. However, you haven't asked the user to authorize it yet. Since you also need to grant offline access, you should request additional permissions through your client-side application. Keep in mind that you already need an active session.
const googleOauth = gapi.auth2.getAuthInstance();
const newScope = "https://www.googleapis.com/auth/calendar"
googleOauth = auth2.currentUser.get();
googleOauth.grantOfflineAccess({ scope: newScope }).then(
function(success){
console.log(JSON.stringify({ message: "success", value: success }));
},
function(fail){
alert(JSON.stringify({message: "fail", value: fail}));
});
You're done with the front-end changes and you're only missing one step. To create a Google API's client in the back-end with the googleapis library, you need to use the refresh token from the previous step.
For a complete workflow with a Node.js back-end, you might find my gist helpful.
While authentication (sign in), you need to add "offline" access type (by default online) , so you will get a refresh token which you can use to get access token later without further user consent/authentication. You don't need to grant offline later, but only during signing in by adding the offline access_type. I don't know about platform.js but used "passport" npm module . I have also used "googleapis" npm module/library, this is official by Google.
https://developers.google.com/identity/protocols/oauth2/web-server
https://github.com/googleapis/google-api-nodejs-client
Check this:
https://github.com/googleapis/google-api-nodejs-client#generating-an-authentication-url
EDIT: You have a server side & you need to work on behalf of the user. You also want to use Google for signing in. You just need #2 Google Sign-in for server-side apps , why are you considering both #1 & #2 options.
I can think of #2 as the proper way based on your requirements. If you just want to signin, use basic scope such as email & profile (openid connect) to identify the user. And if you want user delegated permission (such as you want to automatically create an event in users calendar), just add the offline access_type during sign in. You can use only signing in for registered users & offline_access for new users.
Above is a single authentication flow.

Password protect a page with Firebase

I'm building a CMS with Firebase, but struggling to assess whether what I require is possible, or if I'm missing something.
What I require is the ability to password-protect a page only, and remember that browser as having access. A full user account (using the in built auth) is required to edit the content of the page, but only a password is required to view it.
I know I can use the auth flow with email, but am looking for the editor to be able to create a password for viewing only.
Is this possible, or should I look elsewhere?
The way I commonly do this is a bit like Jeremy's answer, but simpler.
You ask the user for a password when they enter the page, and store that password locally (for reloads).
Then you store data in your database under a path that includes the password. So say that your password is geheim, you could store the data under:
data: {
geheim: {
value: "This is the secret value"
}
}
Now you secure your database with rules like these:
{
"rules": {
".read": false,
"data": {
"geheim": {
".read": true
}
}
}
}
Now somebody can only read the data at /data/geheim if they know the entire path. So you'll enter the data part in your code, but require them to enter geheim as the password. Then you attach a listener with:
firebase.database().ref("data").child(password).once("value", function(snapshot) {
console.log(snapshot.val());
});
And if the user entered the correct value for password, this will read the value.
Firebase Authentication only deals with authenticated user accounts. It doesn't deal with simple password protection of content.
It's definitely possible, but as Doug's answer indicated, you'll have to do it outside normal means. Off the top of my head, the way I would accomplish this is...
When a user enters a password, it stores the password in their local storage.
On page load, or on password entry... pull the password from local storage
Make a request to a Firebase cloud function, makes sure to include the password it just retrieved from local storage, and which page it is requesting content for
Firebase cloud function validates password
Firebase cloud function retrieves data for specific page
Firebase cloud function returns said data
Load data on front-end like normal
As you already identified, you should stick with the built-in Firebase auth for content editing.
I definitely suggest Frank's answer because it's simple and it works. Btw the moral of the story is that you use the firebase Database to store you view-only password but, if you want to complicate your life because you need a strong view-only password system, the Authentication product provides the custom authentication method that you can integrate with your existing auth system (for example fb login). It obviously needs a server-side implementation that is a code that takes the password, check if it's valid and sends the token back to the Auth system.
Here more details: https://firebase.google.com/docs/auth/web/custom-auth

Nodemailer, Heroku, Gmail, invalid login - works locally

I have been having this issue for the past couple of weeks, it works every time locally, however once I deploy it to my heroku server, it will give me an invalid login error. I have gone into the account and givin access to less secure apps. And the credentials are correct, and it works on localhost every time. Is there something I am missing?
quickSendMail: function(routeBody, callback) {
//configuring the nodemailer for email notifications
var smtpConfig = {
host: 'smtp.gmail.com',
port: 465,
secure: true, // use SSL
auth: {
user: 'mysmtpemail123',
pass: '******'
}
};
var mailOptions = {
from: 'SageStorm Site <mysmtpemail123#gmail.com>',
to: ['my email'],
subject: routeBody.subject,
html: 'my message'
};
var transporter = nodemailer.createTransport(smtpConfig);
transporter.verify(function(error, success) {
if (error) {
console.log(error);
} else {
console.log('server is ready to send emails');
}
})
transporter.sendMail(mailOptions, function(error, info) {
if (error) {
console.log(error);
return callback(err, null);
} else {
console.log('Message sent: ' + info.response);
return callback(null, info);
}
})
}
You can allow machines to access your gmail remotely using this link, but keep in mind that Google will affect your default account (even if you are connected with another one).
The easy way: use a incognito / private window (to have no google account connected) and log in your google account and then use the link above.
If that doesn't work try updating your nodemailer to the latest version.
According to https://support.google.com/accounts/answer/6009563, Go to https://accounts.google.com/b/0/DisplayUnlockCaptcha and allow access and try again ..
Also according to docs at nodemailer (https://github.com/nodemailer/nodemailer#delivering-bulk-mail)
I'm having issues with Gmail Gmail either works well or it does not
work at all. It is probably easier to switch to an alternative service
instead of fixing issues with Gmail. If Gmail does not work for you
then don't use it.
I set a bounty to this question, but after looking through the Nodemailer docs/Github, it seems that using Gmail will only lead to headaches.
According to the Nodemailer docs...
"...Gmail expects the user to be an actual user not a robot so it runs a lot of heuristics for every login attempt and blocks anything that looks suspicious to defend the user from account hijacking attempts.
For example you might run into trouble if your server is in another geographical location – everything works in your dev machine but messages are blocked in production."
I believe the second thing was my issue; my Nodemailer ran without issue on my machine, but immediately failed when I pushed the app to Heroku.
As for a fix, my only fix was to use a different email provider. All email accounts I've tried using have been fully authorized to allow other sign-ins and less safe apps; I've gone through every method Google has to offer to allow usage of the account, and I fully believe that it's simply not possible to get consistent results with Nodemailer and Gmail.
Using Outlook/Hotmail worked for me. I also tried switching to a different Gmail account. It worked successfully a couple times, then returned to the previous state.
Most times when one ends up with this kinda error from Nodemailer, one of these options listed by Google gets to fix it, take your time to go through the google account to be used has the required setting.
Google has listed all the potential problems and fixes for us. In as much as you turned on less secure apps setting. Be sure you are applying these to the correct account.
Step 1: Check your password
If you have these problems or can’t sign in, first check to make sure you’re using the right password.
Step 2: Try these troubleshooting steps
If you've turned on 2-Step Verification for your account, you might need to enter an App password instead of your regular password.
Sign in to your account from the web version of Gmail at https://mail.google.com. Once you’re signed in, try signing in to the mail app again.
Visit Display Unlock Captcha and sign in with your Gmail username and password. If asked, enter the letters in the distorted picture.
Your app might not support the latest security standards. Try changing a few settings to allow less secure apps access to your account.
Make sure your mail app isn't set to check for new email too often. If your mail app checks for new messages more than once every 10 minutes, the app’s access to your account could be blocked.

Is it possible to post to chat.postMessage as any user in a Slack team?

I'm building a Slack integration that is intended to modify some text and then post it to a Slack channel as though the user who triggered the command had said it.
e.g. /makeFace disapproval
#Ben 3:45pm
ಠ_ಠ
I ask for the client permission scope, which adds the chat:write:user permission. But when I hit the chat.postMessage endpoint, it only seems to allow you to post as the user who added the integration because the token it returns seems to be individuated for that user.
I know that giphy, for instance, sends its gif messages as though you are the originator, but I can't find out how they manage it. Is there any documentation for sending messages as other members of the team?
There are 2 ways to achieve this:
A. Overwriting username and icon
When you send a message with chat.postMessage it is possible to set a user name with the property username. The message will then appear as being send by that user (same for icon with icon_url).
However, this is not meant to impersonate real users, so even if you use the same username and icon as the real user the message will have the app tag, so that they can be distinguished from a real user.
Here is an example how it looks like (from a gamer Slack about flying and killing space ships):
But depending on what your requirements are that might work for you.
If you want to use it make sure to also set the as_user property to false (yes, really) and it will not work with a bot token, only with a user token.
See here for more details on how it works.
This also works for the legacy version of Incoming Webhooks, not with the current version of incoming webhooks though. (You can still get the legacy version, see this answer)
B. Having the user's token
Another approach is to always use the token from the respective user for sending the message. In combination with as_user = true messages sent by your app will look exactly as if they would come from the respective user (no APP tag).
To make that happen your app would need to collect tokens from all users on your workspace and store them for later use. This can be done by asking every user to install your app (called adding a "configuration") through the Oauth process (same you use to install your app to a workspace), which allows your app to collect and store those tokens for later use.
Update: This doesn't work. It impersonates the user who installed the app, so it merely seems to work... until another user tries to use it (and they end up impersonating you).
Go to your App's management page. Select "OAuth & Permissions".
Add the chat.write OAuth Scope to your app as a User Token Scope, not a Bot Token scope.
Take note of your User OAuth Token at the top of this page (not your But User OAuth Token).
Call chat.postMessage with
username = user id of the user you'd like to post on behalf of
token = the token from step 3. above
The resulting post will be 100% impersonated. Not just the name and icon as mentioned in other answers, but it'll 100% function as if it came from the user.
I hope this will help those who are still facing this issue.
First give the chat:write and chat:write.customize scope to your bot. The scope chat:write.customize Send messages as #your_slack_app with a customized username and avatar
From "OAuth & Permissions" settings get the bot OAuth token or even bot access token (both will work).
Then set the arguments like the following.
username to specify the username for the published message.
icon_url to specify a URL to an image to use as the profile photo alongside the message.
icon_emoji to specify an emoji (using colon shortcodes, eg. :white_check_mark:) to use as the profile photo alongside the message.
You can visit the docs from here

Token google analytics and relationship webPropertyId / profilId

I am currently developing a dashboard with Google Analytics API, which will be accessible website back office. I realized this during this developing javaScript I block on 2 things:
The first is the authentication must be transparent to the user via the use of a token.
In my approach I utlise OAuth2 of the API by generating a token with the playground for this token to be valid
I join my code
gapi.analytics.ready(function() {
var CLIENT_ID = 'XXXX.apps.googleusercontent.com';
var CLIENT_SECRET ='XXX...';
var ACCESS_TOKEN = 'XXX...';
var REFRESH_TOKEN ='XXXX....';
var EXPIRE_IN ='3600';
var TOKEN_TYPE ='Bearer';
var ACCESS_TYPE ='offline';
var SCOPE = 'https://www.googleapis.com/auth/analytics.readonly'
gapi.analytics.auth.authorize({
clientid: CLIENT_ID,
client_secret:CLIENT_SECRET,
serverAuth: {
access_token: ACCESS_TOKEN,
refresh_token: REFRESH_TOKEN,
//token_type: TOKEN_TYPE,
//expires_in: EXPIRE_IN,
//access_type: ACCESS_TYPE,
}
});
After the validity of the data are more accessible with a 401 error (logical because the token is no longer valid)
or to my first question about how to obtain a valid token all the time?
My second question concerns the recovery of data I based on the recovery of the profile number (like many such works).
However SEVERAL of my sites using the tracking number (UA-XXXXXXXX-N).
Knowing that sites use this number is the posibility to find the profilId thanks to the tracking number and accountId that lion can deduct.
But I do not know how to arive.
Es that someone already out how to make this relationship ???
Pending your answers thank you in advance
(Sorry for the translation I utlise google translation)
Authenticating using the playground is a bad idea, and wont work for long. You are going to have to code your own authentication process here. It sounds like you want to do this with your own websites this your own data, I would normally recommend you use a service account. A service account can be set up to authenticate without requiring the user to do anything. While some people say that you can use a Service account with JavaScript, I don't feel that it is a secure solution, I also wonder if it is ok to do this under the current terms of service. So my first recommendation to you is to look into using as service account with a server sided scripting language. say PHP. If you don't a user will have to authenticate and then they will only be seeing the information on there own website not your website.
Second how to find the Profile id:
The first and probably easiest option would be to just go to the admin section of Google analytics and find your profile id there. If you are looking for a way of doing this programmatically you, I would use the account summaries report from the Management API this will give you a list of all of the accounts for the current authenticated user you can then scan that to find the profile ids you want.

Categories

Resources