I have a request to Quickbooks online in order to implement the proper single sign on (SSO) flow. I am using openid4java and getting back the first name, last name, and e-mail address just fine, but no realm id.
I used the published Intuit code as an example, my fetcher looks like this:
final FetchRequest fetch = FetchRequest.createFetchRequest();
try {
fetch.addAttribute("FirstName", "http://axschema.org/namePerson/first", true);
fetch.addAttribute("LastName", "http://axschema.org/namePerson/last", true);
fetch.addAttribute("Email", "http://axschema.org/contact/email", true);
fetch.addAttribute("RealmId", "http://axschema.org/intuit/realmId", true);
} catch (MessageException e) {
mLogger.warning(e.getLocalizedMessage());
e.printStackTrace();
}
My Auth request looks like:
openid.ns:http://specs.openid.net/auth/2.0
openid.claimed_id:http://specs.openid.net/auth/2.0/identifier_select
openid.identity:http://specs.openid.net/auth/2.0/identifier_select
openid.return_to:http://localhost:8888/qbopenid
openid.realm:http://localhost:8888/qbopenid
openid.assoc_handle:o5SH!IAAAACLd1tm9oBJwwIhLZnb4uHYSvphLvPKupx_kJsjquipfQQAAAAEgqUSt5DXPSGwQ79I7lFlHh8gGWpBFaXdjr6diKdticDtCpv8EgpBpO3lDHIhT-275K8lDxZWoeLxqABigxYjf
openid.mode:checkid_setup
openid.ns.ext1:http://openid.net/srv/ax/1.0
openid.ext1.mode:fetch_request
openid.ext1.type.FirstName:http://axschema.org/namePerson/first
openid.ext1.type.LastName:http://axschema.org/namePerson/last
openid.ext1.type.Email:http://axschema.org/contact/email
openid.ext1.type.RealmId:http://axschema.org/intuit/realmId
openid.ext1.required:FirstName,LastName,Email,RealmId
But when the request is returned I don't see any mention of the realm id either in the request. It's almost like it goes missing. Any idea what's wrong? Thanks!
Please see the note here:
https://developer.intuit.com/docs/0025_quickbooksapi/0010_getting_started/0030_integrate_your_app/0030_signing_into_and_out_of_intuit_from_within_your_app/sign-in/implement_sso_and_openid_in_your_app
This field can only be fetched when there is an active connection between the realm and the app and when the user is launching the application from the App Center or navigating to the application from the Blue Dot menu.
You will be returned the realm at the end of the OAuth flow to associate it to the access tokens. It is not always returned by OpenID.
Related
I built a custom authentication system using FirebaseAuthentication tokens.
My signup / login flow should work like this:
User presses login button
My server generates the authentication token and sends it to the client
Check if the user already exists (in the 'Auth' table or in my database?)
If true: sign in using FIRAuth.auth()?.signIn(withCustomToken:...
If false: Show a form to to enter custom information (name, etc..)
sign using FIRAuth.auth()?.signIn(withCustomToken:...
save the custom information to my database
My question is: How can I find out if the user has already signed up?
Would a publicly accessible database with only uid's be the way to go?
This is fairly opinion based, but yes, I would use a standalone DB that stores each user's username who has signed up. Then all that is required is a quick web request through a PHP file querying for any rows returned with that username.
The firebase sign in method will feedback in asynchronous callback.
FIRAuth.auth()?.signInWithEmail(email, password: password, completion: { (user , error) in
if let error = error {
print(error.localizedDescription)
return
}
self.signedIn(user)
})
If you haven't sign up yet. The error will print out
There is no user record corresponding to this identifier. The user may have been deleted.
I'm using Sails with Passport for authentication. I'm using passport-google-oauth(OAuth2Strategy) and passport-facebook for enabling Google Sign-in.
I'm not too well-versed with Passport, so pardon me if this is a rookie question. I've set up login via Facebook and it works just fine. With Google, I do receive an authorization code after allowing access to the app, but the I'm eventually not authenticated. I'm guessing the same code should work for both Facebook and Google since the strategies are both based on oauth2.
I'm not even sure what code to share, since I'm using the auto-generated code from sails-generate-auth, but do let me know if there's anything else I can share.
Any ideas on why this might be happening? The app is locally hosted but that's unlikely to be the problem since I am getting to the authorization stage anyway.
I faced the same problem and it was located here in in api/services/passport.js:
// If the profile object contains a list of emails, grab the first one and
// add it to the user.
if (profile.hasOwnProperty('emails')) {
user.email = profile.emails[0].value;
}
// If the profile object contains a username, add it to the user.
if (profile.hasOwnProperty('username')) {
user.username = profile.username;
}
// If neither an email or a username was available in the profile, we don't
// have a way of identifying the user in the future. Throw an error and let
// whoever's next in the line take care of it.
if (!user.username && !user.email) {
return next(new Error('Neither a username nor email was available'));
}
The Google service was not returning a profile.username property.
Because of it, the user is not saved in the database and cannot be authenticated. Then the passport callback receives an empty user, so the function that handles errors is fired and the user is redirected to the login page.
This change allows to use the displayName property as the username:
// If the profile object contains a list of emails, grab the first one and
// add it to the user.
if (profile.hasOwnProperty('emails')) {
user.email = profile.emails[0].value;
}
// If the profile object contains a username, add it to the user.
if (profile.hasOwnProperty('username')) {
user.username = profile.username;
}
/** Content not generated BEGIN */
// If the username property was empty and the profile object
// contains a property "displayName", add it to the user.
if (!user.username && profile.hasOwnProperty('displayName')) {
console.log(profile); // <= Use it to check the content given by Google about the user
user.username = profile.displayName;
}
/** Content not generated END */
// If neither an email or a username was available in the profile, we don't
// have a way of identifying the user in the future. Throw an error and let
// whoever's next in the line take care of it.
if (!user.username && !user.email) {
return next(new Error('Neither a username nor email was available'));
}
You could also use the profile.id property because profile.displayName is not necessarily unique (ie: two Google accounts can have an identical displayName). But it is also true accross different services: a Twitter account could also have the same username than a Facebook account. If both register on your application, you will have a bug. This is a problem from the code generated by sails-generate-auth and you should adapt it with the behavior that you want.
I will propose a PR if this solution works for you too.
Alright, so this ultimately turned out to be a known issue with the API.
TL;DR: Enable the Google+ API and the Contacts API as mentioned here. (The Contacts API isn't required, as #AlexisN-o pointed out in the comments. My setup worked as desired with Contacts API disabled. This obviously depends on what scope you're using.)
I believe it's not a nice way of failing since this was an API error that was prevented from bubbling up. Anyway, I dug into passport.authenticate to figure out what was going wrong. This eventually calls the authenticate method defined in the package corresponding to the strategy (oauth2 in this case). In here (passport-google-oauth/lib/passport-google-oauth/oauth2.js) I found that the accessToken was indeed being fetched from Google, so things should be working. This indicated that there was a problem with the requests being made to the token urls. So I ventured a little further into passport-oauth2/lib/strategy.js and finally managed to log this error:
{ [InternalOAuthError: failed to fetch user profile]
name: 'InternalOAuthError',
message: 'failed to fetch user profile',
oauthError:
{ statusCode: 403,
data: '{
"error": {
"errors": [{
"domain": "usageLimits",
"reason": "accessNotConfigured",
"message": "Access Not Configured. The API (Google+ API) is not enabled for your project. Please use the Google Developers Console to update your configuration.",
"extendedHelp": "https://console.developers.google.com"
}],
"code": 403,
"message": "Access Not Configured. The API (Google+ API) is not enabled for your project. Please use the Google Developers Console to update your configuration."
}
}'
} }
This was the end of the hunt for me and the first result for the error search led to the correct answer. Weird fix though.
I've got a Meteor application with a multi-phase sign-up process. The accounts are based on the accounts-password package. In the step prior to account creation, the user needs to provide some profile information.
I'd like the user to be able to launch a Facebook OAuth flow which pre-populates the profile fields with information pulled from Facebook.
This all needs to happen pre-account-creation. I want to implement this with the facebook package that backs accounts-facebook.
At the moment I've got the OAuth flow happening by calling Facebook.requestCredential, but I'm not sure how to get an OAuth access token from the credential token that comes back. I suspect I need to pass this to the server and make an API call to get back an access token.
Any pointers as to how this should work would be much appreciated.
Facebook.requestCredential(function (credentialTokenOrError) {
if (credentialTokenOrError && credentialTokenOrError instanceof Error) {
// Error...
console.log(credentialTokenOrError);
} else {
// Credential Token string
console.log(credentialTokenOrError);
// Now perhaps a Meteor.call to a server method that
// 1. Retrieves an access token
// 2. Hits the graph API to get profile information and returns it to the client
}
});
Thanks,
Chris
I was having the same trouble of converting a credentialToken to an accessToken, only with Github. I've written up a gist that has code that should work very similarly. Essentially, there are two steps:
Within your Facebook.requestCredential callback function, call OAuth._retrieveCredentialSecret(tokenOrError), the result of which is the credentialSecret. Then use Meteor.call, passing in tokenOrError and credentialSecret, to call the Meteor.method you'll set up in the next step.
code (on client):
Github.requestCredential({
loginStyle: 'popup',
requestPermissions: ['gist']
}, function(tokenOrError) {
if (tokenOrError && tokenOrError instanceof Error) {
// Throw a Meteor error
console.log('error getting the token');
return;
}
var credentialSecret = OAuth._retrieveCredentialSecret(tokenOrError);
Meteor.call('getGithubAccessToken', tokenOrError, credentialSecret, function(err, accessToken) {});
});
On the server, set up a Meteor.method that takes your credentialToken and credentialSecret and calls Facebook.retrieveCredential. This function returns a credentials object from the _pendingCredentials Mongo Collection before deleting it from the collection. The access token is credentials.serviceData.accessToken. The credentials object could potentially be persisted in the user object in the Meteor.users collection (as it is in the accounts packages) or sent back to the user.
code (on server):
Meteor.methods({
getGithubAccessToken: function(credentialToken, credentialSecret) {
var credentials = Github.retrieveCredential(credentialToken, credentialSecret);
console.log('accessToken:', credentials.serviceData.accessToken);
return credentials.serviceData.accessToken;
}
});
I'm unfamiliar with the specifics of Facebook's Graph API so after these steps, you're on your own. Good luck!
I'm a newbie to app development. I am building a Windows Phone 8.1 app and have followed the tutorial here: http://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-dotnet-backend-windows-store-dotnet-get-started-users-preview/ to add authentication using Facebook. Everything seems to work fine, except that every now and again it appears to stop bringing back any data from my Azure database. Further investigation revealed that the UserId that is being shown from the code below, changes periodically (although I can't quite work out how often it changes).
// Define a member variable for storing the signed-in user.
private MobileServiceUser user;
...
var provider = "Facebook";
...
// Login with the identity provider.
user = await App.MobileService.LoginAsync(provider);
// Create and store the user credentials.
credential = new PasswordCredential(provider,
user.UserId, user.MobileServiceAuthenticationToken);
vault.Add(credential);
...
message = string.Format("You are now logged in - {0}", user.UserId);
var dialog = new MessageDialog(message);
dialog.Commands.Add(new UICommand("OK"));
await dialog.ShowAsync();
This code is identical to the code in the tutorial. The Facebook app settings (on the Facebook developers site) confirm that I am using v2.3 of their API so I should be getting app-scoped UserIds back. I have only ever logged in with one Facebook account, so I would expect the UserId to be the same each time, but they're not. The UserId is prefaced with 'sid:', which someone on the Facebook developers group on Facebook itself says stands for Session ID, which they would expect to change, but if that's the case, I can't work out where to get the actual UserId from that I can then store in my database and do useful things with. I'm sure I must be doing something basic wrong, but I have spent hours Googling this and cannot (unusually) find an answer.
Any help would be greatly appreciated.
Thanks!
So dug deeper. This is how Mobile Apps work (I was thinking from a Mobile Services perspective). The issue here is that the Gateway doesn't provide static SIDs, which is what User.userId provides. The work around to this is listed in the migration doc.
You can only get the Facebook AppId on the server.
ServiceUser user = (ServiceUser) this.User;
FacebookCredentials creds = (await user.GetIdentitiesAsync()).OfType< FacebookCredentials >().FirstOrDefault();
string mobileServicesUserId = creds.Provider + ":" + creds.UserId;
You should note, that this Id is directly connected with your Facebook App registration. If you ever want to migrate your App to a new Facebook App, you'd have to migrate them. You can also use the Facebook AppId to look up the user's global facebook Id via the Facebook Graph API, which you could use between applications. If you don't see yourself using multiple apps, etc., you can use the Facebook AppId just fine.
Hard to tell what's going on to cause you to use a SID instead of the Faceboook token (which like Facebook:10153...).
It may be faster to rip out the code and reimplement the Auth GetStarted. Maybe you missed a step or misconfigured something along the way. If you have the code hosted on github, I can try to take a look.
Another thing you can do is to not trust the user to give you their User id when you save it to a table. On your insert function, you can add it there.
function insert(item, user, request) {
item.id = user.userId;
request.execute();
}
That should, theoretically, be a valid Facebook token. Let me know if that doesn't work; can dig deeper.
Well i developing a Flex desktop app and i cant logout form facebook. I mean after loggin in and updating the photo i want to update, i run the method to log out, which looks like this
FacebookDesktop.logout(handleLogout);
Where handleLogout is a function where i can do other things.
The method runs but never log out. I think that maybe loading an other request i could log out, and i find that using:
"https://www.facebook.com/logout.php?" + info.get_accessToken() +
"&next=http://www.Google.com"
would log out, but i dont know where i ca get the accesToken.
Thanks in advance!
The following code is implemented in for asp.net page using C# code.
EXPLANATION
First you need to send a request to authenticate the user(the IF part). You will get a "CODE" on successfull authentication. Then send a request with this code to authorize the application. On successful authorization you will get the access token as response.
protected void Page_Load(object sender, EventArgs e)
{
if (Request.QueryString["code"] != null)
{
Response.Redirect("https://graph.facebook.com/oauth/access_token?client_id=CLIENT_ID&redirect_uri=CURRENT_URL&client_secret=APP_SECRET&code="+Request.QueryString["code"]);
}
else
{
Response.Redirect("https://www.facebook.com/dialog/oauth?client_id=CLIENT_ID&redirect_uri=CURRENT_URL&scope=read_stream");
}
}
HERE IS THE PROCEDURE
Create an asp.net website
In the default.aspx page implement the above code.
Replace CLIENT_ID,APP_SECRET with the AppId and AppSecret respectively
CURRENT_URL should be the url of the page in which you are implementing the code.
The part "&scope=read_stream" is not mandatory. If you need any additional permissions please enter it here as comma separated values.
You will get a string in the format
access_token=ACCESS_TOKEN_VALUE&expires=EXPIRY_TIME
as response.
Try this to send a POST request using flex
var urlLoader:URLLoader = new URLLoader();
var request:URLRequest = new URLRequest("https://www.facebook.com/logout.php?next=YOUR_URL&access_token=ACCESS_TOKEN");
request.data = binaryData;
request.method = URLRequestMethod.POST
urlLoader.load(request);