I am using app token found here
https://developers.facebook.com/tools/accesstoken/
under my newly created app(2nd line)
var graph = new facebook.GraphAPI(accessToken);
graph.getConnections('me', 'friends', print);
function print(error, data) {
console.log(error ? error : data);
}
Why is it still throwing the error? What am I missing?
An active access token must be used to query information about the
current user.
What you need is a User Token, not an App Token. There is no relation to any user with the App Token, you need to authorize a user with the user_friends permission to get his friends. Keep in mind that you will only get friends who authorized your App with user_friends too, for privacy reasons.
More information about Tokens:
https://developers.facebook.com/docs/facebook-login/access-tokens
http://www.devils-heaven.com/facebook-access-tokens/
You may find this article helpful for the authorization part: http://www.devils-heaven.com/facebook-javascript-sdk-login/
For non-restricted Pages, you can just use an App Token though: https://graph.facebook.com/page-id/videos?limit=20&access_token=myappid|mysecret
...of course you can also use the vanity name instead of the ID.
Related
I am writing an App and am trying to leverage Google for A&A. The app itself relies on access to the users Google Calendar, and so initially I leveraged their updated OAUTH2 library for A&A.
Here is my flow:
User goes to the index.html which has "https://accounts.google.com/gsi/client" script and google.accounts.oauth2.initCodeClient is called with my client_id, scopes, redirect url
<script src="https://accounts.google.com/gsi/client"></script>
<script>
let client;
function initClient() {
client = google.accounts.oauth2.initCodeClient({
client_id: 'xxxxx-xxxx.apps.googleusercontent.com',
scope:
'https://www.googleapis.com/auth/userinfo.profile \
https://www.googleapis.com/auth/userinfo.email \
https://www.googleapis.com/auth/calendar.readonly \
https://www.googleapis.com/auth/calendar.events',
ux_mode: 'redirect',
redirect_uri: 'http://localhost:5000/oauth2callback',
});
}
// Request an access token
function getAuthCode() {
client.requestCode();
}
The user clicks the login button, which kicks off requestCode() and they begin the login flow. They login or select their google account, then besides the unapproved app screen, they get to the consent screen with my requested scopes.
After, they are redirected to my expressjs endpoint and using the "googleapis" library I exchange with id_token for the access and refresh tokens.
...
const { tokens } = await oauth2Client.getToken(req.query.code); //exchange code for tokens
const userInfo = (
await oauth2Client.verifyIdToken({
idToken: tokens.id_token,
audience: config.google.clientID,
})
).payload;
if (!indexBy.email[userInfo.email]) { // check if user exists
const newUser = {
name: userInfo.name,
email: userInfo.email,
o_id: userInfo.sub,
picture: userInfo.picture,
r_token: tokens.refresh_token,
};
...
Ok, all good.... but not quite. The problem is, that next time the user wants to login to the app, they go through the entire flow again, including the consent screen (again).
So, after going through more docs, even looking at examples from google. I was surprised and I noticed that many of those apps used the passport oauth2 plugin :( Something i've done in the past, but was hoping to avoid that with the recently updated Google client and nodejs libraries.
Ok, how to not prompt for consent screen on subsequent logins?
Maybe separate A&A, so first I use "Sign In With Google" for Authentication, then when I get the user info, check if the user is already registered (hence I have already saved the refresh token) and they start the app.
On the other hand, if they are new (not in existing app user collection), after authenticating, I will then call the OAUTH2 authorization redirect, so again they on Googles site, this time to do the scopes api confirmation.
So, first question, is that the best practice with most apps with leverage a Google API via OAuth? To first Authenticate, then possibility Authorize (as needed). Hopefully this will still work ok when things come up with expired/invalid refresh token (fingers crossed the default google library handles that).
When doing the Authorize for consent, can I pass something from the previous Authenticate flow so they don't need to do that again.
Or maybe when doing the Authenticate process (Google Identity Service), there is some flag or param so that if they have already consented, they don't have to do that again on subsequent logins.
Incase I wasn't clear, in a nutshell the question is: should I be doing Authenticate for login, separately from Authorization (oauth2 token). Or should I go right into the Authorization flow, which first Authenticates the user, and can I skip the Authorization consent screens if they've already done that. Or maybe there's another way which is the best practice.
Thanks for your attention.
Background info
Authentication is the act where by a user logs in into a system using their login and password. With authentication we know that the user is behind the machine. For this we use Open id connect, which was built on top of Oauth2. Open id connect returns and id_token which can be used to identify the user, it is often a jwt containing some claims to identify the subject or the user behind the Authentication.
The scope used for open id connect is profile and email. open id connect grants you consent to access a users profile information.
This is an example of the decrypted id token returned by google from a simple call using profile scope only. All this id token is telling you is who the user behind the machine is.
{
"iss": "https://accounts.google.com",
"azp": "4074087181.apps.googleusercontent.com",
"aud": "4074087181.apps.googleusercontent.com",
"sub": "1172004755672775346",
"at_hash": "pYlH4icaIx8PssR32_4qWQ",
"name": "Linda Lawton",
"picture": "https://lh3.googleusercontent.com/a-/AOh14GhroCYJp2P9xeYeYk1npchBPK-zbtTxzNQo0WAHI20=s96-c",
"given_name": "Linda",
"family_name": "Lawton",
"locale": "en",
"iat": 1655219027,
"exp": 1655222627
}
In the same call google also returned an access token. Now my call contained only the scope for profile, due to the fact that its open id connect. This means that I will only have access to the data that the profile scope would grant access to. In this case most of what is behind the Google people api.
Note: The user does not see a consent screen with open id connect, even though they are consenting to profile scope. It is assumed by signing into your account that the system you are logging into would have access to your profile info.
Authorization
Authorization is the process by which a user grants your application authorization to access their private user data. The user is shown a consent screen where they consent to your application accessing data defined by some scopes.
In the case of google calendar api there are serval
https://www.googleapis.com/auth/calendar See, edit, share, and permanently delete all the calendars you can access using Google Calendar
https://www.googleapis.com/auth/calendar.events View and edit events on all your calendars
https://www.googleapis.com/auth/calendar.events.readonly View events on all your calendars
https://www.googleapis.com/auth/calendar.readonly See and download any calendar you can access using your Google Calendar
https://www.googleapis.com/auth/calendar.settings.readonly View your Calendar settings
In this case you are only given an access token this is again Oauth2 it is authorization to access the users calendar data it is not authentication this is not related to login.
Your question
So, first question, is that the best practice with most apps with leverage a Google API via OAuth? To first Authenticate, then possibility Authorize (as needed).
You would do both at the same time.
When you authencation your user make sure to include your google calendar scope then the access token and refresh token returned will grant you access to google calendar.
I am going to assume that you have some kind of user system. When you store the user be sure to store the refresh token that is returned.
As far as Authentication goes i will assume you either have a remember me system which will set a cookie on their machine and remember the user so that you can then get the refresh token from their system the next time they come back.
If they did not chose to select a remember me option then will then have to login every time they visit your site but part of the login will return the "sub": "1172004755672775346", this is the users id on google system so you can use that in your database to match the user when they come back.
Your question is quite complex and will depend upon the type of system you have what it is designed to do as well as what programming language you are using. That being said I hope this very long answer clears things up a bit.
I have a React Native app that uses Strapi for its main API.
Some of the API endpoints require authentication so I've used the Auth0 provider and that's all working fine.
A user is now able to log in and I'm securely storing their access_tokens.
So far, Auth0 only gives me an access_token, a refresh_token, an id_token (jwt containing name and email etc) and expiry times for the tokens.
But I'm wondering if it's possible to be able to store a users preferences like whether they prefer dark or light theme etc and extra info such as a user_id in Strapi and let them update it after logging in with Auth0.
The catch is that only that user should have read/write access to their own data.
I can't see any docs or guidance on this kind of thing. Has anyone else managed to implement this kind of thing and if so, a rough approach would be great!
Thanks!
Well, one way of the doing this is creating a OAuthUsers collection in strapi, which will hold basic details of a user like:
first_name
last_name
email
When a user registers on Auth0 and returns back to your site, you can take the basic details that were returned from the identity management platform and store it in strapi under the OAuthUsers collection.
Now, coming to your question on how to store the preferences of the user, what you can do is create another collection called preferences with following attributes:
is_dark_theme
OAuthUser (Make this a one-to-one relation with OAuthUsers collection )
Every time a logged in user updates his preferences it will first come and create an entry in this collection if not already existing. How you can check if an entry exists for a user is by using the email from the JWT token itself, that you attach as the bearer token on the API calls. I will assume, you already know how to decode a JWT token.
So a rudimentary design would be like so:
const is_dark_theme = request.body.is_dark_theme; // 1 or 0 for light theme
const user = await strapi.services.OAuthUsers.find({ email: '[email from JWT]'});
const preference = await strapi.services.preferences.find({ OAuthUser: user.id });
if(preference)
await strapi.services.preferences.update({is_dark_theme}, {id: preference.id});
else
await strapi.services.preferences.create({is_dark_theme, user: user.id});
So per this, what will happen is the user will only be able to update his own details and never be able to touch the preferences of other users as the user will only be able to pass the is_dark_theme parameter from front end and rest of the information will be taken from the JWT token.
In this code snippet (firebase doc) they have mentioned do not use user.getUid() to authenticate with your backend server. use FirebaseUser.getToken() instead.
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
if (user != null) {
// Name, email address, and profile photo Url
String name = user.getDisplayName();
String email = user.getEmail();
Uri photoUrl = user.getPhotoUrl();
// The user's ID, unique to the Firebase project. Do NOT use this value to
// authenticate with your backend server, if you have one. Use
// FirebaseUser.getToken() instead.
String uid = user.getUid();
}
getUid() // A unique user ID, intended as the user's unique key across all providers.
getToken() // The Firebase authentication token for this session.
My requirement is.
First I will register user with firebase authentication method (Email and password).
I will save String uid = user.getUid(); in my own backend server once registration is successful.
User credit information say user balance is saved in my own backend server as key user.getUid().
User sign-in with Email and password and ask for his balance.
I will get user.getUid() from firebase and match with my records, if match found returns balance to user.
They said getUid() is unique user id but Do NOT use this value to authenticate with your backend server.
Why so? Why can't we use getUid() to authenticate with your backend server??
The uid is a unique identifier for the user. So, while it identifies the user, it does not authenticate them.
A simple corollary is that my Stack Overflow ID is 209103. Knowing that, you can identify me. But to prove to Stack Overflow that you are Frank van Puffelen, requires that you know my credentials.
The ID of a user is quite often exposed in the interface. For example, if you click on my profile, you will see my ID. This is necessary for identifying me. If you would also use that same ID to authenticate, everyone who had seen your profile once could impersonate you on the site. Not a good idea when it comes to security.
Take your requirements as an example, if you using [GET] https://your-domain.com/api/users/$uid/balance to retrieve user's data, then this API is not secured at all, anybody could get other user's data with a random $uid.
As the comment(firebase doc) recommends, FirebaseUser.getToken() will get a JWT token, you should validate the token with firebase Admin SDK in your backend, and that is the data you could trust.
And the method client-side method should update to user.getIdToken() by now.
https://firebase.google.com/docs/auth/admin/verify-id-tokens is the reference for more detail.
So I saw here: https://firebase.google.com/docs/auth/web/account-linking#link-auth-provider-credentials-to-a-user-account That it is now possible to link user accounts in Firebase. I also saw that Firebase provides the functionality of anonymous authentication, where it creates a user session for a user, without any credentials.
In our application we normally use CustomAuthentication with Firebase, as we have our own authentication service. This works perfectly, and we are able to use the same Auth system in-between systems that use Firebase, and the ones that don't.
Now we got to the point where we wanted to take advantage of the anonymous authentication of Firebase, to allow users to use the apps without registering, and just transfer their details after they log in. So I thought that account linking is what I need. But I can't find a way to link an anonymous account with a custom authentication account. Is something like this possible with Firebase?
I have not found linking between anonymous and custom signIns too, so I just use signInAnonymously and pass it's uid to signInWithCustomToken.
Step 1.
To be able acheive this, you should grab an uid from signInAnonymously function like this:
var uid;
auth().signInAnonymously().then(user => {
uid = user.uid;
});
Step 2.
Save current user data like this:
database().ref(`users/${uid}`).set({ some: 'data' });
Step 3.
Send this uid to your server which returns custom token. On your server you just pass this uid to firebase.auth().createCustomToken(uid) function and send this custom token back. It will contain the uid.
Step 4.
When you receive custom token, you can sign in with it like this:
auth().signInWithCustomToken(authKey);
And that's it. You are signed in with your custom token and can access your anonymous user data saved on the step 2.
I had a similar problem but I believe Firebase does not allow this type of linking because it would require linking two separate firebase user ids: the id of the custom token created user and the id of the anonymous user.
Firebase docs say:
Users are identifiable by the same Firebase user ID regardless of the authentication provider they used to sign in.
So handling linking between two user ID's would cause inconsistencies.
I got around this problem by doing a merge of the data between the two accounts.
For example (from Firebase docs):
// Get reference to the currently signed-in user
var prevUser = auth.currentUser;
// Sign in user with another account
auth.signInWithCredential(credential).then(function(user) {
console.log("Sign In Success", user);
var currentUser = user;
// Merge prevUser and currentUser accounts and data
// ...
}, function(error) {
console.log("Sign In Error", error);
});
Caveat: I've never done this. Have you seen this page? Down at the bottom, under the heading "Email-password sign-in", it lists this code fragment:
var credential = firebase.auth.EmailPasswordAuthProvider.credential(email, password);
You can then (apparently) link the credential like this:
auth.currentUser.link(credential).then(function(user) {
console.log("Anonymous account successfully upgraded", user);
}, function(error) {
console.log("Error upgrading anonymous account", error);
});
i am using this link
https://api.instagram.com/v1/users/[user-id]/relationship?access_token=[ACCESS-TOKEN]
in the above link user_id is the id of the person to whom i want to follow and Access token is created for my app in instagram developer site.
and it give response like
{
"meta":
{
"error_type":"OAuthPermissionsException",
"code":400,
"error_message":"This request requires scope=relationships, but this access token is not authorized with this scope. The user must re-authorize your application with scope=relationships to be granted write permissions."
}
}
i searched on internet and some one told me to add scope=relationships while creating access token i don't know how to do this..
Simple Words
Follow a Instagram user on click of a button .
scope=likes+comments+relationships+basic
use it then you are trying to login
"To request multiple scopes at once, simply separate the scopes by a space. In the url, this equates to an escaped space (“+”)."
https://instagram.com/developer/authentication/