I'm using Torii and ember-simple-auth to manage authentication on my front-side, and Knock and Omniauth-twitter on my server. I had no problem with Facebook, but Twitter using Oauth1.0, I have troubles to figure out the flow.
Here is my Torii config :
# environment.js
ENV['torii'] = {
sessionServiceName: 'sessiontorii',
providers: {
'facebook-oauth2': {
apiKey: 'API_KEY',
redirectUri: 'http://localhost:4200/'
},
'twitter': {
requestTokenUri: 'http://127.0.0.1:3000/auth/twitter'
}
}
My route or controller :
# route.js
twitterLogin() {
var self = this;
this.get('sessiontorii').open('twitter').then(function(response) {
console.log(response);
self.transitionTo('index');
}, function() {
console.log('auth failed');
});
},
A new window is opening and I can login with my Twitter account. My server does the authentication/registration, but I can't figure out how to close this new window and send the token to my front.
Is my flow completely wrong ? Or do I miss something ?
I followed this tutorial, but I wonder if it's not a bit outdated
The issue was that I was sending a wrong type of data from my server. So I updated my torii-provider and the code I was sending. Torii does the job and close the new window when it gets the data. Then, I'm sending the data to my authenticator and confirm the authentication with the JWT code.
Related
I tried to create a react native app with OAuth login in my rails app.
I have this react native setup
const config = {
issuer: 'http://app.domain.tld',
clientId: '85bb84b9cb0528b1f64b7c77586507b3ca5e69b11abe36ae1e54e88a6150c21e',
clientSecret: '0d3c0713437e0028a121a0c2294cc9a72f4eb5609416935a2860e20f176c7855',
redirectUrl: 'com.domain://com.domain',
responseType: 'code',
scopes: [],
dangerouslyAllowInsecureHttpRequests: __DEV__,
// clientAuthMethod: 'post',
serviceConfiguration: {
authorizationEndpoint: 'http://app.domain.tld/oauth/authorize',
tokenEndpoint: 'http://app.domain.tld/oauth/token',
// revocationEndpoint: 'http://app.domain.tld/oauth/revoke'
},
additionalParameters: {
approval_prompt: 'force'
}
}
// use the client to make the auth request and receive the authState
try {
const result = await authorize(config)
// result includes accessToken, accessTokenExpirationDate and refreshToken
console.log('result', result)
} catch (error) {
console.log('error', error.message)
}
the oaut app looks like this:
When I press the button to login in my app, I get a browser window where I can login in my rails app and got redirected to the authorize page for the oauth app. After I authorize my app, I got redirected to the app with the message "Network error". With that message I can't find out whats wrong, maybe anyone can help me.
In the server logs I can see this:
Redirected to com.domain://com.domain?code=54f3b4c03ea3724522f9a7983e2ea1b9037336076cd52cb875f9654d5d79784a&state=8xmiicVcPKN980ZDZUwBnw
in the debugger-ui i get this error log:
error Error: Network error
at createErrorFromErrorData (ReactNativeART.js:10)
at ActivityIndicator.js:72
at MessageQueue.__invokeCallback (ReactNativeART.js:472)
at blob:http://localhost:8081/1fccf34b-97b0-4c42-81fa-f0e1391a3ad3:2358
at MessageQueue.__guard (ReactNativeART.js:373)
at MessageQueue.invokeCallbackAndReturnFlushedQueue (blob:http://localhost:8081/1fccf34b-97b0-4c42-81fa-f0e1391a3ad3:2357)
at debuggerWorker.js:80
so i think there is something wrong. The app didn't make any further request to the server to obtain the access_token. Whats wrong?
Use this in config:
dangerouslyAllowInsecureHttpRequests: true,
I am newbie to electron and I am currently trying to implement an OAuth2.0 API which requires a callback URI. Url callback requires valid URL (https://myserver.com/sucess). so i tried this code snippet but does not work.
// Your GitHub Applications Credentials
var options = {
client_id: 'your_client_id',
client_secret: 'your_client_secret',
scopes: ["user:email", "notifications"] // Scopes limit access for OAuth tokens.
};
app.on('ready', () => {
// Build the OAuth consent page URL
var authWindow = new BrowserWindow({ width: 800, height: 600, show: false, 'node-integration': false });
var githubUrl = 'https://github.com/login/oauth/authorize?';
var authUrl = githubUrl + 'client_id=' + options.client_id + '&scope=' + options.scopes;
authWindow.loadURL(authUrl);
authWindow.show();
function handleCallback (url) {
console.log(url);
}
// Handle the response from GitHub - See Update from 4/12/2015
authWindow.webContents.on('will-navigate', function (event, url) {
handleCallback(url);
});
authWindow.webContents.on('did-get-redirect-request', function (event, oldUrl, newUrl) {
handleCallback(newUrl);
});
// Reset the authWindow on close
authWindow.on('close', function() {
authWindow = null;
}, false);
});
also, i used angular js route but does not work either.
so I'm wondering if there is a way to run server inside electron app to serve app from URL (https://localhost:3000) and if so how this will affect app behavior at packaging and distributing time, i means does the app will run from the same port
... any suggestions will help about how i can approach this problem. thank you
I had the same issue last week, i needed to integrate my electron app with vkontakte api which uses form of OAuth protocol. What you can do:
1) You launch local node http server, probably in separate process as i did.
2) You request code through oauth link and set redirect uri as http://127.0.0.1:8000/, for some reason https://localhost didn't work for me.
3) In main process you wait for message with code from server, on server implemented corresponding logic (when you receive request and code in it send through process.send back to parent message with code)
4)You request access token from main process, you shouldn't change redirect_uri. You again catch response from your server.
5) You get access_token, you kill server...
But when i did all this i read their docs till end and there was stated that standalone apps, like mine for desktop could receive token in easier way through "implicit flow", and you can get your token with only one call. Hope my experience could be extrapolated on your issue. Good luck!
I'm trying to use the github-oauth2 provider in Torii, but I'm stumped on how I'm supposed to se tup some of the callbacks. I'll trace the code I'm using, as well as my understanding of it, and hopefully that can help pinpoint where I'm going wrong.
First, in my action, I'm calling torii's open method as it says to do in the docs:
this.get('torii').open('github-oauth2').then((data) => {
this.transitionTo('dashboard')
})
And, of course, I have the following setup in my config/environment.js:
var ENV = {
torii: {
// a 'session' property will be injected on routes and controllers
sessionServiceName: 'session',
providers: {
'github-oauth2': {
apiKey: 'my key',
redirectUri: 'http://127.0.0.1:3000/github_auth'
}
}
},
}
The redirectUri is for my Rails server. I have the same redirectUri setup on my github app, so they match.
Here's what I have on my server. It's likely this is where the problem is. I'll get to the symptoms at the end.
def github
client_id = 'my id'
client_secret = 'my secret'
code = params[:code]
#result = HTTParty.post("https://github.com/login/oauth/access_token?client_id=#{client_id}&client_secret=#{client_secret}&code=#{code}")
#access_token = #result.parsed_response.split('&')[0].split('=')[1]
render json: {access_token: #access_token}
end
So I post to github's access_token endpoint, as I'm supposed to, and I get back a result with an access token. Then I package up that access token as json.
The result of this is that the torii popup goes to the rails page:
Unfortunately, what I was hoping for was for the torii popup to disappear, give my app the access_token, and for the code to move on and execute the code in my then block.
Where am I going wrong?
Many thanks to Kevin Pfefferle, who helped me solve this and shared the code to his app (gitzoom) where he had implemented a solution.
So the first fix is to clear my redirectUri, and to set it on github to localhost:4200. This made the app redirect so that it's an Ember app that it's redirected to.
The second fix was to create a custom torii provider
//app/torii-providers/github.js
import Ember from 'ember';
import GitHubOauth2Provider from 'torii/providers/github-oauth2';
export default GitHubOauth2Provider.extend({
ajax: Ember.inject.service(),
fetch(data) {
return data;
},
open() {
return this._super().then((toriiData) => {
const authCode = toriiData.authorizationCode;
const serverUrl = `/github_auth?code=${authCode}`;
return this.get('ajax').request(serverUrl)
.then((data) => {
toriiData.accessToken = data.token;
return toriiData;
});
});
}
});
Not sure why this then triggers but the then I was using before didn't. Anyways, it grabs the data and returns it, and then the promise I was using before gets the data correctly.
this.get('torii').open('github-oauth2').then((data) => {
//do signon stuff with the data here
this.transitionTo('dashboard')
})
So there we go! Hopefully this helps other folks who are stuck in the future.
I am using the cloudspace angularjs-devise library on the client. When I try to login/register I get a 200 ok response with the plain user object visible in the chrome js console. Refreshing the page seems to lose this information even though I assumed that the service would store this at some point since it also has logout and currentUser methods.
https://github.com/cloudspace/angular_devise
My questions are:
1) Is this service actually storing the user and if so how (i.e. with cookies or localstorage or in memory)?
2) If the service does not store the user how can I store this information in a custom cookie/localstorage and more importantly set the user into the service so that the services "isauthenticated" and "currentuser" methods can be used?
Partial Library Readme Instructions
Just register Devise as a dependency for your module. Then, the Auth service will be available for use.
angular.module('myModule', ['Devise']).
config(function(AuthProvider) {
// Configure Auth service with AuthProvider
}).
controller('myCtrl', function(Auth) {
// Use your configured Auth service.
});
Auth.login(creds): Use Auth.login() to authenticate with the server. Keep in mind, credentials are sent in plaintext; use a SSL connection to secure them. creds is an object which should contain any credentials needed to authenticate with the server. Auth.login() will return a promise that will resolve to the logged-in user. See AuthProvider.parse() for parsing the user into a usable object.
angular.module('myModule', ['Devise']).
controller('myCtrl', function(Auth) {
var credentials = {
email: 'user#domain.com',
password: 'password1'
};
Auth.login(credentials).then(function(user) {
console.log(user); // => {id: 1, ect: '...'}
}, function(error) {
// Authentication failed...
});
});
My partial code:
main.js
var myApp = angular.module('mail_app', ['ngRoute', 'ngResource', 'Devise']);
myApp.config(function($routeProvider, $locationProvider, $httpProvider, AuthProvider) {
console.log("in router")
$locationProvider.html5Mode(true);
$httpProvider.defaults.headers.common['X-CSRF-Token'] =
$('meta[name=csrf-token]').attr('content');
$httpProvider.defaults.headers.common['ClientType'] = 'browser';
// Customise login
AuthProvider.loginMethod('POST');
AuthProvider.loginPath('/api/v1/users/login.json');
// Customise register
AuthProvider.registerMethod('POST');
AuthProvider.registerPath('/api/v1/users.json');
});
SessionsController.js
myApp.controller('SessionsController', ['$scope', 'Auth', '$http', function($scope, Auth, $http) {
console.log("in session controller")
console.log(Auth.isAuthenticated());
$scope.loginUser = function() {
console.log("in login")
var credentials = {
email: $scope.email,
password: $scope.password
};
Auth.login(credentials).then(function(user) {
$scope.authError = 'Success!';
console.log(user); // => {id: 1, ect: '...'}
Auth.currentUser = user;
}, function(error) {
$scope.authError = 'Authentication failed...';
});
};
$scope.registerUser = function(){
console.log("in register function")
var ncredentials = {
email: $scope.newEmail,
password: $scope.newPassword,
password_confirmation: $scope.newPasswordConfirmation
};
Auth.register(ncredentials).then(function(registeredUser) {
console.log(registeredUser); // => {id: 1, ect: '...'};
}, function(error) {
$scope.authError = 'Registration failed...';
});
};
$scope.getCurrentUser = function(){
Auth.currentUser().then(function(user) {
// User was logged in, or Devise returned
// previously authenticated session.
console.log(user); // => {id: 1, ect: '...'}
$scope.id = user.id;
}, function(error) {
// unauthenticated error
});
};
$scope.isUserAuthenticated = function(){
Auth.isAuthenticated();
};
}]);
First of all you need to understand how cookies and sessions work in Rails.
From this article:
Rails uses a CookieStore to handle sessions. What it means is that all
the informations needed to identify a user's session is sent to the
client and nothing is stored on the server. When a user sends a
request, the session's cookie is processed and validated so rails,
warden, devise, etc. can figure out who you are and instantiate the
correct user from the database.
What this means is that on every request, Rails will look up at the session cookie, decode it and get something like
cookie = {
"session_id": "Value",
"_csrf_token": "token",
"user_id": "1"
}
At that point Rails knows that the current user has id=1 and can make a sql query. (Like current_user = User.find(1)).
When a user is logged in, a cookie is created, when the user is logged out - the cookie is destroyed. If Rails doesn't find a cookie or the cookie doesn't have information about the current user, devise will assume that the user is not logged in (current_user is nil)
Even if you login through ajax (to be particular it is through the 'angular_devise' gem in your case) the cookie is created. It is not stored on the server, but in the browser. (This is why if you are logged in one browser, you are not automatically logged in another browser) As you pointed out the library doesn't keep information who is logged in, and this is because the information is stored in a cookie and the library cannot decode the cookie without help from the server.
This is why you will have to make a call to get the current user if the user refreshes the page. (Sorry)
The way to get the current_user is very simple. This is the cleanest solution I found.
# application_controller.rb
def me
render json: current_user
end
# routes.rb
get "me" => "application#me"
// main.js
// I am not familiar with angular_devise lib but you get the point:
// this method fetches from server when myApp is initialized (e.g. on page reload)
// and assigns the current_user so he/she can be used by the app
myApp.run(["AuthService", function(AuthService) {
AuthService.getUserFromServer();
}]);
If you have to load data specific to the user, you will have to load the user first and then the data. Needless to say you will have to use promises.
TL;DR: You will have to ask the server
I am open for questions and comments.
I guess your problem is the refresh. The angular-devise lib is probably assuming you are in a SPA (Singe Page Application) so it should not refresh. With this assumption, angular-devise can store all the information in memory. When you refresh your page, you basically bootstrap the application from zero. And the request to server is probably issued by your code when application is starting. You probably call Auth.currentUser() somewhere on start of the application
Had same problem. Just use that gem
https://github.com/jsanders/angular_rails_csrf
You can also get rid of "protect_from_forgery" in your application controller, but this is very risky.
I got some code by doing search which is doing a lot for me in showing the my tweets in tableview,till now fine. I want to add one more functionality to it that user can post the message from the sameapp.
So I just modified the code as per. While I hit the request I got result status as successful but message is not posting to my wall. I have all keys and getting access token as well.
var client = Twitter({
consumerKey: "have Key ",
consumerSecret: "have Key",
accessTokenKey: accessTokenKey,
accessTokenSecret: accessTokenSecret
});
client.request("1/statuses/update.json", {status:'TEST'}, 'GET', function(e) {
if (e.success) {alert(e.success);
} else {
alert(e.error);
}
Updated: I have go through the Twitter Dev API
This is the URL http://api.twitter.com/1/statuses/update.format with required parameter "status". What am I doing wrong?
You are sending GET request to update status whereas twitter api needs it to be a POST request.
Try something like
client.request( "1/statuses/update.json", {status:'TEST'}, 'POST', function(e) {
if (e.success)
{
alert(e.success);
} else {
alert(e.error);
}
Check out this application: https://github.com/appcelerator-titans/tweetanium
From what I understand this is a fully working example of a twitter app created using Titanium Mobile. Perhaps you can follow the logic in here and see where you need to adjust.