how to store sub account auth tokens twilio - node.js

I am using the MERN stack for an app im building. In this app im using twilio. I have decided to use twilio sub-accounts. The way this works is I create a MASTER twilio account that give me an accountSid and authToken.
I can store these as ENV variables in Heroku when I want to deploy, and anytime I need to access these ENV vars I can just use the process.env.AUTH_TOKEN in my Node.js server.
Every customer that signs up for my app is going have their own subaccount that is a child of my MASTER account. When this sub account is created, It will give that user their own accountSid and authToken.
This is where my issue stands, Do I need to store each users authToken on Heroku as an ENV variable?
ex..
process.env.USER_1_AUTH_TOKEN
process.env.USER_2_AUTH_TOKEN
process.env.USER_3_AUTH_TOKEN
process.env.USER_4_AUTH_TOKEN
process.env.USER_5_AUTH_TOKEN
I dont think this will work because how will I know which users auth token belongs to them?
Currently in Development I am storing the sub-account authToken directly on the user object, this user object is visible to the client side of the app and im worried that exposing the auth token directly to the client could result in some sort of hack?
Is it safe to store the auth token on the user object directly in mongodb and whenever my react app needs the user, just send the user object without the auth token?
Should I create a auth-token Model, and store a document in the auth-token model containing the auth-token and user_id and everytime I need the auth token just query mongodb for the auth-token with user_id as a parameter?
How does one go about storing say 100,000 of these auth-tokens?
I'm worried about security and twilio docs dont say much about this...

According to the Subaccounts API documentation you can use the Twilio rest API to instantiate a subaccount and assign that subaccount a friendly name that is easy to retrieve.
client.api.v2010.accounts
.create({friendlyName: 'Submarine'})
.then(account => console.log(account.sid));
This returns an object that contains a lot of information but it has a unique SID for that is associated to that new number/subaccount. That object is then also linked back to your main account via the owner_account_sid which is attached to that object.
Twilio provides functionality in the subaccount API to allow you to retrieve a subaccounts data based on the friendly name like so...
client.api.v2010.accounts
.list({friendlyName: 'MySubaccount', limit: 20})
.then(accounts => accounts.forEach(a => console.log(a.sid)));
So what you should be doing is as follows...
Create a naming convention within your system that can be used to form friendly names to assign to subaccounts.
Use the API to create a new subaccount with the Twilio API under the friendly naming convention you've developed.
Anytime you want to make a call, text, or other supported action from the Twilio API first perform an API action to look up the SID of that account by the friendly name.
Grab the sid the friendly name returns and attach it to your client object like so require('twilio')(accountSid, authToken, { accountSid: subaccountSid });
Perform your action through that client using the subaccount Sid that is now attached.

I would like to add some additional info for anyone using twilio subaccounts.
So what I did was create a master account with twilio. This gives you an accountSid and authToken.
These you can store in Heroku under config vars.
When you create your login function for a user via passport login, google-passport or some custom login you create, make your api call to create a subaccount. ( Like when you buy numbers, you buy them under your main account and drill them to your sub accounts) When this is created ONLY add the sub account accountSid to your user object. the sub accountSid is worthless without the auth-token and since you need to make an api call with your env vars your sub account auth tokens are safely stored in twilio.
Now whenever you need to make twilio api calls, say for sending a message or making a phone call etc... first make a call to this endpoint
client.api.v2010.accounts(subActServiceSid)
.fetch()
.then(account => account.authToken);
const accountSid = keys.accountSid // master accountSid
const authToken = keys.authToken // master authToken
// these will be stored in heroku
const listChatMessages = async (req, res) => {
const { subActServiceSid } = req.user // getting sub accountSid from user object
const subActAuthToken = client.api.v2010.accounts(subActServiceSid)
.fetch()
.then(account => account.authToken);
const subClient = require('twilio')(subActServiceSid, subActAuthToken)
await subClient.messages.list({ // make all the api calls your heart desires
from: chat.phone
})
.then( messages => messages.forEach((m) => { console.log("message", m})
this will contain a JSON object with the sub accounts authToken. You can then use this authToken for the API call. No need to worry about storing 100,000 users authTokens somewhere.... If this is still confusing message me.

Related

Can an open id connect id token be used to authenticate to an api

I am building a mern application.
the backend built using express exposes an api which users can create data and access the data they have created.
I want to allow users to sign in with google and get authorization to create and access the resources on this api which i control (not on google apis).
I keep coming across oauth 2 / open id connect articles stating that an Id token is for use by a client and a access token provided by a resource server should be used to get access to an api.
e.g. https://auth0.com/blog/why-should-use-accesstokens-to-secure-an-api/
the reason stated for this is that the aud property on the id token wont be correct if used on the api.
I realise that some sources say: that if the spa and api are served from same server and have same client id and therefore audience I can use and id token to authenticate to the api, but I am looking to understand what I can do when this is not the case?
I feel using oauth2 for authorization is overkill for my app and I cant find any information about how to use open id connect to authenticate to my api.
Surely when you sign in to Auth0 authourization server using google it is just requesting an open id connect id token from google?
I am wondering if using Authorization Code Grant flow to receive an id token on the api server would allow me to authenticate a user to my api?
in this case would the api server be the client as far as open id connect is concerned and therefore the aud value would be correct?
I can generate an url to visit the google oauth server using the node googleapis library like so:
const { google } = require("googleapis");
const oauth2Client = new google.auth.OAuth2(
'clientid','clientsecret',
"http://localhost:3000/oauthcallback",//this is where the react app is served from
);
const calendar = google.calendar({ version: "v3", auth: oauth2Client });
const scopes = ["openid"];
const url = oauth2Client.generateAuthUrl({
// 'online' (default) or 'offline' (gets refresh_token)
access_type: "offline",
// If you only need one scope you can pass it as a string
scope: scopes,
});
async function getUrl(req, res) {
console.log(url)
res.status(200).json({
url,
});
}
and use the following flow.
You are not supposed to access any API's using the ID-Token. First of all the life-time of the ID-token is very short, typically like 5 minutes.
You should always use the access-token to access API's and you can using the refresh token get new access-tokens. The ID-token you can only get one time and you use that to create the local user and local cookie session.
If you are using a SPA application, you should also consider using the BFF pattern, to avoid using any tokens in the SPA-Application
see The BFF Pattern (Backend for Frontend): An Introduction
I agree with one of the commenters that you should follow the principle of separation of concern and keep the authorization server as a separate service. Otherwise it will be a pin to debug and troubleshoot when it does not work.

Method Creating custom tokens will create uid if not exist in firebase

I using firebase. I using method: Creating custom tokens. When i look document in firebase https://cloud.google.com/identity-platform/docs/admin/create-custom-tokens .
const uid = 'some-uid';
admin
.auth()
.createCustomToken(uid)
.then((customToken) => {
// Send token back to client
})
.catch((error) => {
console.log('Error creating custom token:', error);
});
I have a question
If i don't have uid in firebase, it will create it ? If i have uid, i only create custom token and send back to client. I look document and i don't see document specific it. Thanks
There is no need to make an API call to create a user in Firebase when you're using custom authentication. Calling createCustomToken on the server, and then signing on the client with the resulting ID token, is all that is needed.
There is no separate step to create the UID for a custom provider. You create the custom token with the UID and other claims you want, send it to the client over a secure connection, and the client can then sign in to Firebase with that token.
For Firebase services the ID token, and the UID in it, are ephemeral - since it is your server (where you run the Admin SDK), and the client that maintain them. That's the reason a separate step to create the UID in Firebase is not necessary.

How often should I get a new service sid of twillio?

When using twillio, how often should the service sid be created and create a new services? I see 3 options. There might be more options thought.
Get the service sid/create a new service and store it in the db, then never get a new one.
Get the service sid/create a new service every time the backend server starts/runs (I'm using node/express)
Get the service sid/create a new service whenever using the twillio client is used
Here's the code:
let serviceSid = '';
async function init() {
const service = await client.verify.services.create({
friendlyName: 'someName'
});
serviceSid = service.sid;
}
Also, what's the purpose of it, if you already have an account sid?
Your Twilio ACCOUNT SID together width AUTH TOKEN has to do with your overall Twilio account. It gives you access to the entire Twilio API (Verify is just a part of it).
const accountSid = 'ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
const authToken = 'your_auth_token';
const client = require('twilio')(accountSid, authToken);
To use the Twilio Verify product (or service if you wish) you will need to create at least one "verification service". You can do it in the Twilio console or by making API requests.
When you create a verification service you get that SID which starts with "VA" and it will be used in a verification code for example.
client.verify.services('VAXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX')
.verifications
.create({to: '+15017122661', channel: 'sms'})
.then(verification => console.log(verification.sid));
If let's say you have more than one application using Verify you'll want to create a verification service for each application.
Also, maybe the verification message template is different or maybe you expect different lengths for the verification code, etc. then you need separate verification services hence different VA SIDs.
So from your options in your question I would go with number 1 per distinct application. If you only have one app then I would get one verification service per environment (one for dev, one for QA, one for production, etc.).
Also, I would create Twilio sub-accounts for each app + environment and use them instead of the Twilio master account.

CognitoIdentityServiceProvider.signUP() doesn't accept user pool id?

In the aws-sdk cognito documentation there is a function listed called signUp() that quote "Registers the user in the specified user pool and creates a user name, password, and user attributes." However, there is no parameter for a user pool Id. How exactly does one specify the user pool they want to add to? At first glance it thought maybe it was just missing in the documentation, but I tried adding UserPoolId as a property in the parameters object, to which it responded with an error about an unexpected field. There is also no function parameter to accept the pool id. My only other guess was that maybe the CognitoIdentityServiceProvider object accepted it in its constructor, but that also does not appear to be the case. I am aware that the API also provides the function AdminCreateUser() to add users, but don't want to use it if there's a better way.
documentation here https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CognitoIdentityServiceProvider.html#signUp-property
Any ideas?
In Amazon Cognito, the User Pool ID is considered to be a sensitive piece of information, and it is used only in Admin API calls. The SignUp API call is not AWS SigV4 Signed, and it is meant to run on the browser side instead of the server side.
From the App Client ID, Cognito implicitly understands which User Pool you are referring to in your code. Hence, you can use the code in the documentation, and users will get added to your User Pool without the User Pool ID being a parameter in the API call.
Specify the App Client Id from the User Pool - not the User Pool Id. Create an App Client on the User Pool if you don't have one yet.
https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-client-apps.html
The App Client Id is not considered secret from the AWS Cognito docs (but of course App Client Secret is - never include that anywhere on the client side). I've scratched out my App Client Id just to not broadcast here.
import {
CognitoIdentityServiceProvider
} from 'aws-sdk'
// make sure to use the same region as your user pool
const cognitoIdentityServiceProvider =
new CognitoIdentityServiceProvider({region: 'us-west-2'})
try {
const signUpResp =
await cognitoIdentityServiceProvider.signUp({
// Not UserPool ID but the ID for the "App Client" you have created on the User Pool
ClientId: '5orf...i67r',
Password: pw,
Username: email // i'm using email address for Username
}
).promise()
console.log(signUpResp)
} catch(e) {
alert(e.message || JSON.stringify(e));
}

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.

Resources