In Meteor, I believe ordinarily you can get the screenname of a twitter user after they've logged in with {{services.twitter.screenName}}.
However with autopublish turned off the only thing that seems to be available is {{currentUser.profile.name}} (which returns their 'full name' i.e. Kevin Rose, not krose).
How would I go about getting the screenname or avatar from a user that has logged in with Twitter, if autopublish has been removed?
You just need to set up a publish record on the server to determine what information you're going to send to the client, and then subscribe to it in a client-side startup function (or better still, iron-router).
Meteor.publish("userData", function () {
return Meteor.users.find({_id: this.userId},
{fields: {'services.twitter': 1}});
});
That will provide the services field for the logged in user in Meteor.user() once that client has subscribed to "userData" in addition to the fields that are automatically supplied.
You need to manually publish / subscribe your data. By default, only emails, username and profile fields are published for Meteor.users collection (see the docs). So you need to publish others:
Meteor.publish('userData', function() {
if(!this.userId) return null;
return Meteor.users.find(this.userId, {fields: {
services: 1,
...
}});
});
After that, subscribe to this channel on the client:
Deps.autorun(function() {
Meteor.subscribe('userData');
});
Related
For the app I'm building I want the end user to login using gapi OAuth2 and
from there I want the app to look for a playlist on their YouTube channel
and load it.
The getAuthInstance method returns an object with a Google username. However
for my own particular username, a query to find channel id by username
returns no results. From some browsing online, this is apparently an issue
with certain YouTube accounts.
Is there any workaround for this issue?
If you have a valid OAuth 2.0 authentication/authorization (for example, obtained by using GAPI), then it's quite simple the determine the authenticated user's channel ID using the Channels.list API endpoint queried with the parameter mine=true:
mine (boolean)
This parameter can only be used in a properly authorized request. Set this parameter's value to true to instruct the API to only return channels owned by the authenticated user.
Upon invoking the endpoint, the property id of the returned Channels resource contains the channel ID of the authenticated user.
For what concerns a Javascript GAPI (i.e. Google’s Client Library for Browser-side JavaScript) implementation, the code would look like shown below (for a broader context look into this sample source file from Google: analytics_codelab.js):
var channelId;
function loadAPIClientInterfaces() {
gapi.client.load('youtube', 'v3', function() {
getUserChannel();
});
}
function getUserChannel() {
var request = gapi.client.youtube.channels.list({
part: 'id',
fields: 'items(id)',
mine: true
});
request.execute(function(response) {
if ('error' in response) {
displayMessage(response.error.message);
} else {
channelId = response.items[0].id;
}
});
}
Note that the code above (unlike that in analytics_codelab.js) uses the fields request parameter for to obtain from the Channels.list endpoint only the channel's ID info (it is always good to ask from the API only the info that is of actual use).
I'm setting up an action on google project which uses the OAuth & Google Sign In Linking Type.
Previously, I was using the userId that was sent in every request to look up the user in the database to see if there were accesstokens and refreshtokens available. But since userId is deprecated, I am looking for an alternative.
The user starts his/her dialog and then bumps into this piece of code:
app.intent('Give Color', async (conv, { color }) => {
conv.data[Fields.COLOR] = color;
if (conv.user.ref) {
await conv.user.ref.set({ [Fields.COLOR]: color });
conv.close(`I got ${color} as your favorite color.`);
return conv.close('Since you are signed in, I\'ll remember it next time.');
}
return conv.ask(new SignIn(`To save ${color} as your favorite color for next time`));
});
The "To continue, link Test App to your Google Account" on which the user selects the correct Google account.Then my /token endpoint is called on the OAuth server containing the Google ID Token (assertion) which holds all of the users data. I decode it, check in the database if the "sub" is already present, and I throw the following exception:
return res.status(401).send({ error: 'user_not_found' });
Then the normal OAuth procedure kicks in, where I deliver a token to Google. Sidenote: this is my own OAuth Server written in NodeJS. I am sure that the access- and refreshtoken are delivered to Google.
After token delivery, I get a new request on my action:
app.intent('Get Sign In', async (conv, params, signin) => {
if (signin.status !== 'OK') {
return conv.close('Let\'s try again next time.');
}
const color = conv.data[Fields.COLOR];
await conv.user.ref.set({ [Fields.COLOR]: color });
return conv.close(`I saved ${color} as your favorite color. `
+ 'Since you are signed in, I\'ll remember it next time.');
});
The signin.status has a value of "OK". But shouldn't the conv.user object contain the Google ID Token so that I can store the access- and refreshtoken along with this "sub" from the Google ID Token in my database? Or am I getting something wrong?
The content of the conv.user looks like this:
User {raw: Object, storage: Object, _id: undefined, locale: "en-BE", verification: "VERIFIED", …}
_id: undefined
[[StableObjectId]]: 7
access: Access {token: "ACCT-ATlbRmcpMI545WJFssRSlK1Jcza46NIB"}
entitlements: Array(0) []
id: undefined
last: Last {seen: Thu Aug 08 2019 10:53:17 GMT+0200 (Central Europea…}
locale: "en-BE"
name: Name {display: undefined, family: undefined, given: undefined}
permissions: Array(0) []
profile: Profile {token: undefined}
raw: Object {accessToken: "ACCT-ATlbRmcpMI545WJFssRSlK1Jcza46NIB", locale: "en-BE", lastSeen: "2019-08-08T08:53:17Z", …}
storage: Object {}
verification: "VERIFIED"
__proto__: Object {constructor: , _serialize: , _verifyProfile: , …}
conv.user.id is *DEPRECATED*: Use conv.user.storage to store data instead
It won't contain the Google ID of the user, because the user hasn't authorized that.
What they have authorized is whatever you've asked them to authorize via your OAuth server.
So you'll see the access token that your server has sent to the Assistant in conv.user.access, and you can then use this token to lookup who the user is in your database and take action accordingly.
If you specifically want their Google ID, you'll need to make sure that they use Google Sign-In on the same project as your Action (either through voice, a mobile app, or a webapp).
If you just need an ID so you can see when this user returns later, you can use the Google ID you get from Google Sign-In, or just generate an ID and store this in conv.user.storage.
Since I just want to have an ID, I will be using this:
If you just need an ID so you can see when this user returns later, you can use the Google ID you get from Google Sign-In, or just generate an ID and store this in conv.user.storage.
Thanks!
I'm using the Google Login iOS SDK to login, then passing GIDGoogleUser.authentication.idToken to the server, which I'm then verifying in Node JS. The verification in the code below works fine. "payload" var ends up being correct with basic information about the user.
How do I translate the idToken into credentials that I can use to git the people.get endpoint? (I want to know whether the user is using the default Google profile photo or not, and that is available from the people.get endpoint.) This does not seem to be documented anywhere.
https://developers.google.com/people/api/rest/v1/people/get
var auth = new GoogleAuth;
var client = new auth.OAuth2(GoogleUtils.clientIDs, '', '');
client.verifyIdToken(
token,
GoogleUtils.clientIDs,
// Or, if multiple clients access the backend:
//[CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3],
function(e, login) {
if (e) {
return next(e, null);
}
var payload = login.getPayload();
return next(null, payload);
});
Thanks for your help. I can't seem to find any of this info in the documentation. Google's APIs are very poorly documented it seems.
Unfortunately, as noted, the current ID token payload does not say whether the photo is the default one (probably something we should add). If you need an access token to call Google's REST APIs (such as people.get) for more user data, then you need to obtain an OAuth auth code, and exchange it for access and refresh tokens, as documented at https://developers.google.com/identity/sign-in/ios/offline-access
I'm trying to integrate a functionality in a webapp, where a user clicks a button to tweet how awesome the webapp is. (I know, I know, stupid, but hey, the boss wants it)
I set up the route below using Thujohn's Twitter for Laravel 4 - https://github.com/thujohn/twitter-l4
Route::get('/twitter/callback', function() {
// You should set this route on your Twitter Application settings as the callback
// https://apps.twitter.com/app/YOUR-APP-ID/settings
if(Session::has('oauth_request_token')) {
$request_token = array(
'token' => Session::get('oauth_request_token'),
'secret' => Session::get('oauth_request_token_secret'),
);
Twitter::set_new_config($request_token);
$oauth_verifier = FALSE;
if(Input::has('oauth_verifier')) {
$oauth_verifier = Input::get('oauth_verifier');
}
// getAccessToken() will reset the token for you
$token = Twitter::getAccessToken( $oauth_verifier );
if( !isset( $token['oauth_token_secret'] ) ) {
return Redirect::to('/')->with('flash_error', 'We could not log you in on Twitter.');
}
$credentials = Twitter::query('account/verify_credentials');
if( is_object( $credentials ) && !isset( $credentials->error ) ) {
// $credentials contains the Twitter user object with all the info about the user.
// Add here your own user logic, store profiles, create new users on your tables...you name it!
// Typically you'll want to store at least, user id, name and access tokens
// if you want to be able to call the API on behalf of your users.
// This is also the moment to log in your users if you're using Laravel's Auth class
// Auth::login($user) should do the trick.
var_dump($credentials);
//return Redirect::to('/')->with('flash_notice', "Congrats! You've successfully signed in!");
}
return Redirect::to('/')->with('flash_error', 'Crab! Something went wrong while signing you up!');
}
});
However, I don't really know what I should do in this part:
// $credentials contains the Twitter user object with all the info about the user.
// Add here your own user logic, store profiles, create new users on your tables...you name it!
// Typically you'll want to store at least, user id, name and access tokens
// if you want to be able to call the API on behalf of your users.
// This is also the moment to log in your users if you're using Laravel's Auth class
// Auth::login($user) should do the trick.
What do I need to do to make sure a user doesn't need to authenticate himself every time?
If you inspect the $credentials object, you should see oauth access tokens. Save these along with your user data (username, email, name, etc) in your database, and you can use them next time you make a call to the twitter API.
So I've got an question about authentication and have been wondering how other people might handle this situation. I'm currently running an Angular app that is built on a Rails API.
So far for authentication I have a form that does a post to the Rails side which logs the user in and then sends them back to the Angular app on success. Once the cookie is set and the user is logged in, I'm able to access a user.json file which contains all the User information one might expect (Id, username, roles, rights, etc). Since verification all happens on Rails, if the user logs out then this information is removed. So the two states look like so...
Logged in
{
id: 99384,
name: "Username",
url: "//www.test.com/profiles/Username",
timezone: null,
rights: [ ],
roles: [
"admin"
],
}
Logged out
{
error: "You need to login or join before continuing."
}
So far I've seen all these millions of different ways to do auth for Angular, but it seems like nothing fits this type of method. So my question is, since the server is handling all of the verification, is there a way to just check if they user.json file is empty (displaying the error message) and if it is send the Angular app to the Rails login page? Is there really any point messing with Cookies, Tokens, etc when I can base it all on the JSON file?
You are already using cookies - the server is setting them. What you have done is a fairly standard way of doing things.
To check the json file, you can do something like this stub shows in your controller:
app.controller('AppControl', function($scope, $http, $location){
// Get the JSON file.
$http.get('/path/to/json/file')
.then(response){
if(response.data.error){
// redirect to login
$location.path('login');
}
else{
$scope.user = response.data;
// your app code here.
}
})
.catch(function (error){
// unable to reach the json file - handle this.
});
});
Of course, you should really move this out into a service so you can re-use it, and also cache the data, rather than getting the user every time you change route/page, but this gives you a vague idea.
EDIT Example factory:
.factory('User', function( $http ){
// Create a user object - this is ultimately what the factory will return.
// it's a singleton, so there will only ever by one instance of it.
var user = {};
// NOTE: I am assigning the "then" function of the login promise to
// "whenLoggedIn" - your controller code is then very easy to read.
user.whenLoggedIn = $http.get('user.json')
.then(function(response){
// Check to see if there is an error.
if (response.data.error !== undefined) {
// You could be more thorough with this check to determine the
// correct action (examine the error)
user.loggedIn = false;
}
else {
// the user is logged in
user.loggedIn = true;
user.details = response.data;
return user;
}
}).then; // <-- make sure you understand why that .then is there.
return user;
})
Usage in the controller
.controller('ExampleController', function($scope, User){
// It's handy to have the user on the scope - you can use it in your markup
// like I have with ng-show on index.html.
$scope.User = User;
// Do stuff only if the user is loggedin.
// See how neat this is because of the use of the .then function
User.whenLoggedIn( function (user){
console.log(user.details.name + " is logged in");
});
});
Because it's on the scope, we can do this in the html:
<body ng-controller="ExampleController">
<h1 ng-show="User.loggedIn == null">Logging in..</h1>
<h1 ng-show="User.loggedIn == true">Logged in as {{ User.details.name }}</h1>
<h1 ng-show="User.loggedIn == false">Not logged in</h1>
</body>
Here is an example on plunker where this is working.
Note the following:
If the user is/was already logged in, when you inject the service in the future, it won't check the file again. You could create other methods on the service that would re-check the file, and also log the user out, back in, etc. I will leave that up to you.
There are other ways to do this - this is just one possible option!
This might be obvious, but it's always worth saying. You need to primarily handle authentication and security on the server side. The client side is just user experience, and makes sure the user doesn't see confusing or conflicting screens.