How to obtain access token in Cloud Functions for Firebase when user signs up? - swift

When a client signs in with Github, I would like to trigger a Cloud Function that retrieves the user's profile data from Github and stores it in /users/{uid}/profile in the Realtime database.
I can trigger my cloud function when a new user signs up using:
exports.fetchProfile = functions.auth.user().onCreate(event => {
// user = event.data
// uid = user.uid
// access_token = ???
// todo: request user profile from Github using access_token
// todo: save profile in /users/{uid}/profile
});
But, how do I obtain the user's access_token needed for making the Github profile request? Thanks.
What I have tried:
On the client, use FirebaseAuth to get the Github access_token.
Create a Firebase credential using the access_token from (1).
Sign in with the credential from (2), I get a FIRUser in success callback, from which I can obtain uid.
I write {uid: access_token} to a queue in the Realtime database, this in turn triggers my cloud function that does the profile retrieval.
All these just to get the user's access_token, can I do better?

You can't do it with the user creation trigger.
You have multiple options:
after sign up, get the access token from the client and saved it in the real-time database under a location accessible by the specified user. You can then set a cloud functions database trigger for that location. When that is triggered, you get the access token and make your API call to GitHub and get the data and do whatever you want with it.
Run everything on the client side. Since you have the access token after sign up in the client (browser), make the API call to GitHub and then save the profile data in a database location accessible by the user only. You can add a cloud function to trigger on changes to that location if you need to run additional admin operations.
By the way, Firebase Auth (4.0.0) now returns additional user data. Check: https://firebase.google.com/docs/reference/js/firebase.auth#.UserCredential
You can get the additional data by calling result.additionalUserInfo.profile, or the GitHub username: result.additionalUserInfo.username.

Related

Next-auth (JWT) logging sessions

According to Next-auth documentation as we are using credentials provider to connect to our users collection for usernames and passwords Next-Auth does not use the session database to check if a session is active.
If you use a custom credentials provider user accounts will not be persisted in a database by NextAuth.js (even if one is configured). The option to use JSON Web Tokens for session tokens (which allow sign in without using a session database) must be enabled to use a custom credentials provider.
I am wanting to add a _middleware that will allow me to store and check that the latest JWT sessions inside our session database matches the latest one that the user is currently using.
Reason being is that if I have two devices technically I would be able to login on both devices and at the moment their is no real way to discern if the user from PC2 is also login on PC1.
So my theory and not sure if this will work is to add the following.
callbacks: {
jwt: async ({ token, user }) => {
console.log("running JWT - because of custom login")
user && (token.user = user)
(ADD CODE HERE TO SAVE TOKEN & CHECK IF TOKEN IS LATEST TOKEN + VALID - INSIDE SESSION DATABASE)
(IF OLD-TOKEN IS NO LONGER VALID OR THE LATEST TOKEN LOG THE USER OUT)
console.log("TOKEN IS "+ JSON.stringify(token))
return token
},
session: async ({ session, token, user }) => {
console.log(JSON.stringify(session) +" / "+ JSON.stringify(token) +"/"+ JSON.stringify(user));
session.user.tokenID = token //ADD CODE HERE TO SAVE TOKEN TO SESSION COOKIE
session.user = user
return session
}
},
Then if I create a middleware that checks this tokenID and matches it with the session database and if it is the latest result from said user.
For example.
Say PC1 (user1) login here
{
_id: 1
tokenID: 918171-918171-81716-0887
userid: 00-00-00-001
expire: "2022-05-23T12:47:04.593Z"
}
But then PC2 also (user1) login again and created a new session
{
_id: 2
tokenID: 71888-651777-616666-0117
userid: 00-00-00-001
expire: "2022-05-24T12:47:04.593Z"
}
What I would need the middleware to do (which a simple mongodb query could do) is check if their is an older session stored for the same userID if so then logout from PC1.
Now there are a few things that I can see going wrong with this idea.
Other Provider Sessions (which use session DB) making it harder to validate
Every time you call the signup page or session it seems to re-run the JWT section - which in theory is fine, as we could use a findOne Update function which if token is in session then just update the expiry - however that would cause say PC1 refreshes after PC2 logged in then PC1 expire time might be longer then PC2 expire time (but a simple sort function would allow us to see if the ID was older then PC2 if so logout).
JWT changes token every time you reload the page
How would this help privacy and the user data?
By not storing the user details inside a session cookie we would not be exposing the data to hackers or other plugins like FB or Google as the user data would only be linked to a token ID. Which to request the user data you would have to make sure the tokenID was valid first and then be allowed to fetch user data.
I understand that Next-Auth may not want too do this, and this is why I ask the question what is the best practice to do what I am wanting to achieve.
This answer is based on the confirmation that the issue is that you want to be able to only have users able to be signed in to one computer/device at time and that you are using usernames and passwords to authenticate them.
In that scenario you also need to have a database that records a token in each JWT issued. It is not possible to solve for that problem without a database.
Here is how you can solve for it it using JWT and a database:
On every new sign in, you would need to use the jwt callback to add something like a UUID to each JWT and then record that UUID , the User ID and the time the JWT expires in a database.
In that callback, if there are other entries in the database for the same User ID you should mark them as invalid (or delete them from the database).
Everytime an existing JWT is read in that same callback you would need to check to see if the UUID in the database was still valid (i.e. still exist / doesn't point to a UUID that corresponds to a JWT flagged as expired) and if it is no longer valid, don't return a valid JWT.
You might also want to add special handling in the session callback that does something similar to improve the user experience by gracefully handling it in the User Interface of the computer they are being signed out of.
Effectively this has all the downsides of a JWT with all the downsides of a session database (although there are niche cases where this is a preferable approach).
I would not recommend using usernames and passwords or limiting users to only being able to sign in to one computer at at time, however given those unusually specific constraints (which also necessitates a solution that has a negative impact on performance) you may want to consider a different authentication solution and/or think how else you could address the underlying need this is trying to address (and if it's worth the cost and complexity).

Do you need to use the OAuth flow to access your own dropbox account?

I'm writing a dropbox integration against my own account. When file get dropped I respond to a webhook notification and import the files into one of our backend systems.
It's all done in back end server code and there is no real opportunity to pop up a UI to get me to sign in.
I've developed it so far using the access token you can get from the app console but that expires after a few hours.
Are there any auth shortcuts when using the API with just your own account?
I've figured out some shortcuts for myself
First off all as its a background process it needs to be running with "offline" access and using refresh tokens to acquire short lived access tokens.
As there is no UI and its only for my own account I can get an authorisation code via the browser from this URL
https://www.dropbox.com/oauth2/authorize?client_id={{Your APP ID Here}}&token_access_type=offline&response_type=code
then use POSTMAN to get an access Token & Refresh Token:
In Postman:
Post to https://api.dropboxapi.com/oauth2/token
Authorisation Basic
UserName = {{AppKey}}
Password = {{AppSecret}}
Body :x-ww-form-urlencoded
code = <<Auth Code From Browser>>
grant_type = authorization_code
This produces a result with an access_token and a refresh_token
The refresh token will only expire if I withdraw access so that will be safe to add into my config and use to request access tokens or connect to the client.
In my case its c#
using (var dbx = new DropboxClient(refreshToken,appKey,appSecret))
{
var myAccount = await dbx.Users.GetCurrentAccountAsync();
String.Format("{0} - {1}",
myAccount.Name.DisplayName,
myAccount.Email)
.Dump("Account Details");
}

Facebook GraphQL - How to get a users email address and name from backend

I have an app where the user logs into Facebook (and thus has an Auth Token) and then sends that token to my server for authentication within the app.
If it's the users first time in the app, I need to sign them up as well (gather email and name)
Using the users FB auth token (and any server-side tokens) how do I retrieve the user's email address and name? (What endpoints do I need to hit with what tokens/body?)
--
Additional Info:
The login is scoped with ['public_profile', 'email']
The application is running in Node.js on AWS Lambda, and I'd prefer to make a simple fetch if possible instead of installing a whole gql client.
I have tried looking at their graphQL documentation, but I can't
seem to make heads or tails out of it.
I do have access to the user's ID (example: 10157426730xxxxxx)
This would be the API call to get the name and email of a user, with a User Token:
https://graph.facebook.com/me?fields=name,email&access_token=xxxx
Alternatively, you can add the version:
https://graph.facebook.com/v4.0/me?fields=name,email&access_token=xxxx
All the existing fields for users are here to find: https://developers.facebook.com/docs/graph-api/reference/user/
You do not need the User ID, the User Token identifies the User anyway and you can just use "me" instead of the ID. The Graph API is a REST API though, not GraphQL.

Limit user access to api calls?

I am creating a rest api to be the backend for a messaging app. I want a user to only have access to their own data. For example user1 can call /users/user1 but not /users/user2. How would I go about doing this.
So first the user will login and be authenticated via their username and password. But where do I go from here? I was thinking of storing a token with the users data so when they access it I can verify that the pair matches but there must be a better way to do this. Do I need to restructure my api?
After the user logs into the system, you should provide them a token or initialize a session for that user. In each consecutive call, the user should send the token to the API. As long as the token/session is alive user should be able to call the API.
You should have a way to verify the user token in the backend for each API call. A very popular way of doing this is to use JWT(JSON Web Tokens) based authentication.
See here for an example using python and flask: https://realpython.com/token-based-authentication-with-flask/
Once you verify the user, you should parse the user id to the database query in order to filter out the data for that user.
Even though I don't understand your full use case, it seems like you need to restructure your API calls as well. You should not provide API calls per user. What happens when the numbers of users increase in your system dynamically?
So you should either accept user id as a parameter or you should let the JWT authenticator take care of it.
Example REST API call would be
GET /user/data?userId=1234

Looking for some advice on front end/backend user authentication

I'm running a mock expressjs server in the back, and ember (ember-simple-auth) on the front with the ember-simple-auth-token addon. I'm using JWT tokens. I'm trying to decide whats the best way to send my user information. Usually when the user submits their credentials I create a new token, store a copy of it in the database (I'm using mongodb), send it to the frontend and then use the token to fetch information user information. I have a /auth/token (which authenticates and sends the token, makes a copy and stores it in the database) & /current_user route which gets called on the initial login, which uses the token and fetches the user info.
Is it better to simply send the user info in the initial payload of the token over having a separate route? Should I be storing a copy in the database in order to do a comparison and retrieve user information?
Also what are the advantages of a token refresh?
you are using Jwt-Auth for authentication.
-According to my knowledge after sending the user credentials u will respond with the token if credentials are correct otherwise send error.
-why are you saving the token in db ?.
you will send token to client (stateless). If client requests for data then we need to check for token. if it validates then return proper response otherwise return token error.
-why you need token refresh?
for security purpose. After response every time change the token.
TTL your token will be valid for some time (say 60 mins). after that it will be invalid.
This is how JWT works.