auth0 invalid token toward Rails API - ruby-on-rails

I'm trying to set up authentication using the Auth0 lock along with a React single page app and a Ruby on Rails API.
import React from 'react';
import Auth0Lock from 'auth0-lock';
var Login = React.createClass({
componentWillMount: function() {
this.lock = new Auth0Lock('*************', '****.eu.auth0.com', {
allowedConnections: ['facebook']
});
this.lock.on('authenticated', this._doAuthentication.bind(this));
},
showLock: function() {
this.lock.show();
},
_doAuthentication(authResult) {
console.log('Bearer '+authResult.idToken);
var request = require("request");
var options = { method: 'POST',
url: 'http://localhost:3000/authenticate',
headers: { authorization: 'Bearer '+authResult.idToken } };
request(options, function (error, response, body) {
if (error) throw new Error(error);
console.log(body);
});
console.log(authResult);
this.setToken(authResult.idToken)
},
login() {
this.lock.show()
},
loggedIn() {
return !!this.getToken()
},
setToken(idToken) {
localStorage.setItem('id_token', idToken)
},
getToken() {
return localStorage.getItem('id_token')
},
logout() {
localStorage.removeItem('id_token');
},
render: function() {
return (
<div className="login-box">
<button className="btn btn-success" onClick={this.showLock}>Sign In</button>
</div>);
}
});
export default Login;
This code comes from the Auth0 documentation. I also configured knock on the Rails API. Still, whenever I click on the "Connect via Facebook" button, I get the following:
- my token is generated (It's a valid token)
- Request is sent, with the correct authorization header
- Rails returns a 401
I made sure Rails receives the header as "Bearer [MY TOKEN]", still getting a 401.
Did I miss something ? Is anything else required ?

Ok, finally found out: my secret was not 64base encoded, which means the JWT.base64url_decode in my knock.rb was not necessary. I removed it, and voila.

Related

Swagger UI with Swashbuckle not displaying OAuth2 option

I've got a Web API which I've secured with OAuth2 but I'm having difficulty getting Swagger UI to show the authentication option.
Currently, the api_key section of UI still shows, despite there being no configuration for it.
Here's my SwaggerConfig
public class SwaggerConfig
{
public static void Register()
{
if (ConfigUtil.SSOSupported)
{
var thisAssembly = typeof(SwaggerConfig).Assembly;
// Swashbuckle.Application.OAuth2SchemeBuilder
GlobalConfiguration.Configuration
.EnableSwagger(c =>
{
c.Schemes(new[] { "http", "https" });
c.SingleApiVersion("v1", "API Adapter");
c.PrettyPrint();
c.ApiKey(string.Empty);
c.OAuth2("oauth2")
.Description("Description here")
.Flow("implicit")
.AuthorizationUrl(ConfigUtil.SSOAuthority() + "/connect/authorize")
.Scopes(scopes =>
{
scopes.Add("api", "api");
});
c.IgnoreObsoleteProperties();
c.DescribeAllEnumsAsStrings();
})
.EnableSwaggerUi(c =>
{
c.EnableOAuth2Support("your-client-id", "your-client-secret-if-required", "your-realms", "your-app-name");
});
}
}
}
This section is included in page source after loading:
window.swashbuckleConfig = {
rootUrl: 'http://localhost:44390',
discoveryPaths: arrayFrom('swagger/docs/v1'),
booleanValues: arrayFrom('true|false'),
validatorUrl: stringOrNullFrom(''),
customScripts: arrayFrom(''),
docExpansion: 'none',
supportedSubmitMethods: arrayFrom('get|put|post|delete|options|head|patch'),
oAuth2Enabled: ('true' == 'true'),
oAuth2ClientId: 'your-client-id',
oAuth2ClientSecret: 'your-client-secret-if-required',
oAuth2Realm: 'your-realms',
oAuth2AppName: 'your-app-name',
oAuth2ScopeSeperator: ' ',
oAuth2AdditionalQueryStringParams: JSON.parse('{}'),
apiKeyName: 'api_key',
apiKeyIn: 'query'
};
And the iniOAuth function is called:
window.swaggerUi = new SwaggerUi({
url: swashbuckleConfig.rootUrl + "/" + swashbuckleConfig.discoveryPaths[0],
dom_id: "swagger-ui-container",
booleanValues: swashbuckleConfig.booleanValues,
supportedSubmitMethods: swashbuckleConfig.supportedSubmitMethods,
onComplete: function(swaggerApi, swaggerUi){
if (typeof initOAuth == "function" && swashbuckleConfig.oAuth2Enabled) {
initOAuth({
clientId: swashbuckleConfig.oAuth2ClientId,
clientSecret: swashbuckleConfig.oAuth2ClientSecret,
realm: swashbuckleConfig.oAuth2Realm,
appName: swashbuckleConfig.oAuth2AppName,
scopeSeparator: swashbuckleConfig.oAuth2ScopeSeperator,
additionalQueryStringParams: swashbuckleConfig.oAuth2AdditionalQueryStringParams
});
But I just get the api_key text box and no Authorize button like I'd expect.
The controllers and actions all display without an issue.
I'm sure it's something small, but I've been fiddling for a couple of days now and have run out of ideas.

OAuth2 in electron application in current window

I'm trying to implement OAuth2 authentication in Angular 2 ( Electron ) application.
I achieve that on the way with a popup that is called after user click on 'Sign In' button.
In popup user types their credentials and allows the access and on confirm code is returned and I'm able to catch redirect request which I can't do without popup.
Here is implementation that works:
return Observable.create((observer: Observer<any>) => {
let authWindow = new electron.remote.BrowserWindow({ show: false, webPreferences: {
nodeIntegration: false
} });
authWindow.maximize();
const authUrl = AUTHORIZATION_WITH_PROOF_KEY_URL
+ `?client_id=${CLIENT_ID}&response_type=code&scope=api_search&`
+ `redirect_uri=${REDIRECT_URL}&code_challenge=${challenge}&code_challenge_method=S256`;
if (this.clearStorage) {
authWindow.webContents.session.clearStorageData({}, () => {
this.clearStorage = false;
authWindow.loadURL(authUrl);
authWindow.show();
});
} else {
authWindow.loadURL(authUrl);
authWindow.show();
}
authWindow.webContents.on('did-get-redirect-request', (event, oldUrl, newUrl) => {
const code = this.getCode(newUrl, authWindow);
if (!code) {
this.clearStorage = true;
return;
}
this.requestToken({
grant_type: 'authorization_code',
code: code,
code_verifier: verifier,
redirect_uri: REDIRECT_URL
})
.subscribe((response: { access_token: string, refresh_token: string }) => {
observer.next(response);
});
});
// Reset the authWindow on close
authWindow.on('close', () => {
authWindow = null;
});
});
and as you can see in above code I'm creating new BrowserWindow with:
new electron.remote.BrowserWindow({ show: false, webPreferences: {
nodeIntegration: false
} });
and with that approach I'm able to catch up redirect request with a block of code that starts with:
authWindow.webContents.on('did-get-redirect-request', (event, oldUrl, newUrl) => {
....
}
but I'm not able to solve this without popup ( modal ).
Here is my attempt:
return Observable.create((observer: Observer<any>) => {
let authWindow = electron.remote.getCurrentWindow();
const authUrl = AUTHORIZATION_WITH_PROOF_KEY_URL
+ `?client_id=${CLIENT_ID}&response_type=code&scope=api_search&`
+ `redirect_uri=${REDIRECT_URL}&code_challenge=${challenge}&code_challenge_method=S256`;
if (this.clearStorage) {
authWindow.webContents.session.clearStorageData({}, () => {
this.clearStorage = false;
authWindow.loadURL(authUrl);
});
} else {
authWindow.loadURL(authUrl);
}
authWindow.webContents.on('did-get-redirect-request', (event, oldUrl, newUrl) => {
debugger;
// this is not called, I'm not able to catch up redirect request
});
// Reset the authWindow on close
authWindow.on('close', () => {
authWindow = null;
});
});
With my approach I get login screen from remote URL in a current window, but the problem is that I'm not able to catch redirect request with ('did-get-redirect-request') event.
I also tried with 'will-navigate' and many others.
Although I don't have a direct answer I thought I'd point you to Google's AppAuth-JS libraries, which cover OAuth based usage for Electron Apps.
My company have used AppAuth libraries for the mobile case and they worked very well for us, so that we wrote less security code ourselves and avoided vulnerabilities.
There is also an Electron Code Sample.

Handing errors i.e., 401, 403, etc. for Ember Test

Searching for suggestions on handling errors in an Ember test? Found a closed issue from last year, Allowing rejected promises in tests, which indicated that rejections cannot be conveniently handled. Is this still the case?
Right now, in our application, we are using ajax errors to return an unauthorized error if the user is logged out of our main Rails application.
Route
import Ember from 'ember';
import {isUnauthorizedError, isForbiddenError} from 'ember-ajax/errors';
export default Ember.Route.extend({
userService: Ember.inject.service('user'),
activate: function(){
this.get('userService').facility().then(
(data) => {
this.get('controller').set('facility', data.data);
}, (response) => {
if (isUnauthorizedError(response)) {
this.transitionTo('report.failure.401');
} else if (isForbiddenError(response)) {
this.transitionTo('report.failure.403');
} else {
}
}
),
Acceptance Test
test('generates 401 error', function(assert) {
visit('/reporting');
httpStubs.stubUser(server, {}, 401);
httpStubs.stubFacility(server, {});
httpStubs.stubReport(server, '/api/reports/summary.json', []);
click('button.btn-filter')
andThen(function(){
assert.equal(currentURL(), '/users/login');
});
Report Stub
stubReport(server, url, data, status) {
let statusCode = (status) ? status : 200;
server.get(url, function() {
return [
statusCode,
{'Content-Type': 'application/json'},
JSON.stringify(data)
];
});
}

How to get updated session data on rails using AngularJs without page refresh

I'm currently working on integrating devise as an authentication backend with angular as its frontend.
I have faced a problem on when login and logout, the session data will be updated untill the page refresh.
What i will do get session data without page refresh..?
Thanks for your Answers...
AngularJs Controller :
function UsersCtrl($scope, Session) {"use strict";
$scope.CurrentUser = Session.requestCurrentUser();
$scope.login = function(user) {
$scope.authError = null;
Session.login(user.email, user.password)
.then(function(response) {
if (!response) {
$scope.authError = 'Credentials are not valid';
} else {
$scope.authError = 'Success!';
}
}, function(response) {
$scope.authError = 'Server offline, please try later';
});
};
$scope.logout = function() {
// alert("woow");
Session.logout();
};
$scope.register = function(user) {
$scope.authError = null;
console.log(user);
Session.register(user.email, user.password, user.confirm_password)
.then(function(response) {
}, function(response) {
var errors = '';
$.each(response.data.errors, function(index, value) {
errors += index.substr(0,1).toUpperCase()+index.substr(1) + ' ' + value + ''
});
$scope.authError = errors;
});
};
}
AngularJs Session Service:
angular.module('sessionService', ['ngResource'])
.factory('Session', function($location, $http, $q) {
// Redirect to the given url (defaults to '/')
function redirect(url) {
url = url || '/';
$location.path(url);
}
var service = {
login: function(email, password) {
return $http.post('/users/login', {user: {email: email, password: password} })
.then(function(response) {
service.currentUser = response.data.user;
if (service.isAuthenticated()) {
//$location.path(response.data.redirect);
$location.path('/store');
}
});
},
logout: function() {
$http.delete('/sessions').then(function(response) {
$http.defaults.headers.common['X-CSRF-Token'] = response.data.csrfToken;
service.currentUser = null;
redirect('/store');
});
},
register: function(email, password, confirm_password) {
return $http.post('/users', {user: {email: email, password: password, password_confirmation: confirm_password} })
.then(function(response) {
service.currentUser = response.data;
if (service.isAuthenticated()) {
console.log("authenticated");
$location.path('/');
}
});
},
requestCurrentUser: function() {
if (service.isAuthenticated()) {
return $q.when(service.currentUser);
} else {
return $http.get('/users').then(function(response) {
service.currentUser = response.data.user;
return service.currentUser;
});
}
},
currentUser: null,
isAuthenticated: function(){
return !!service.currentUser;
}
};
return service;
console.log(service);
});
One Thing about building applications like this (restful) is that understanding the the backend as an api and app as a front-end very well.
Then think about a story as such;
In the login screen of your app
Front-end: You Provided the credentials to your backend;
Back-end: Checked and authenticated then It will create a unique hash stored in db (JWT recommended to check expiration in frontend) to your Front-end.
Front-end:Save it in a cookie.
Also place it in your ajax setting header part as "Authorization: {token}"
Front-end: Then send each request with this header to your backend.
Back-end: Always check if the token is present and valid to provide resources.
http://www.thebuzzmedia.com/designing-a-secure-rest-api-without-oauth-authentication/ this link has helped me understand the whole thing and misconceptions in the past.
use $window.location.reload(); before page redirect.
One way to achieve this could be overriding the devise sessions_controller destroy action and afrer doing sign_out #current_user return the session as json

gapi.auth.signOut(); not working I'm lost

Below is the code I am using to login with google. I have an element on login.php with id authorize-button. When clicked it logs in just fine.
I have a logout link in my header file. When I click the logout it calls gapi.auth.signOut(); then it destroys session and redirects back to login.php
This happens as far as I can tell but then it just logs the user right back into our site with google. This is a pain as some of our users switch from google to facebook logins.
Thanks in advance for any help.
function handleClientLoad() {
gapi.client.setApiKey(apiKey);
window.setTimeout(checkAuth, 1);
}
function checkAuth() {
gapi.auth.authorize({client_id: clientId, scope: scopes, immediate: true}, handleAuthResult);
}
function handleAuthResult(authResult) {
var authorizeButton = document.getElementById('authorize-button');
if (authResult && !authResult.error) {
//authorizeButton.style.visibility = 'hidden';
makeApiCall();
} else {
//authorizeButton.style.visibility = '';
authorizeButton.onclick = handleAuthClick;
}
}
function handleAuthClick(event) {
gapi.auth.authorize({client_id: clientId, scope: scopes, immediate: false}, handleAuthResult);
return false;
}
function signOut() {
gapi.auth.signOut();
}
function makeApiCall() {
gapi.client.load('oauth2', 'v2', function() {
var request = gapi.client.oauth2.userinfo.get();
request.execute(function(logResponse) {
var myJSON = {
"myFirstName": logResponse.given_name,
"myLastName": logResponse.family_name,
"name": logResponse.name,
"socialEmailAddress": logResponse.email
};
gapi.client.load('plus', 'v1', function() {
var request = gapi.client.plus.people.get({
'userId': 'me'
});
request.execute(function(logResponse2) {
//alert(JSON.stringify(logResponse));
myJSON['profilePicture'] = logResponse2.image.url;
myJSON['socialId'] = logResponse2.id;
//alert(JSON.stringify(myJSON));
$.ajax({
type: "POST",
url: "includes/login-ajax.php",
data: "function=googleLogin&data=" + JSON.stringify(myJSON),
dataType: "html",
success: function(msg) {
if (msg == 1) {
//window.location = "settings.php";
}
}
});
});
});
});
});
}
Make sure you have set your cookie-policy to a value other than none in your sign-in button code. For example:
function handleAuthClick(event) {
gapi.auth.authorize(
{
client_id: clientId,
scope: scopes,
immediate: false,
cookie_policy: 'single_host_origin'
},
handleAuthResult);
return false;
}
Note that sign out will not work if you are running from localhost.
Weird issue, but solved my problem by rendering the signin button (hidden) even if the user is authenticated.
See full question/answer here https://stackoverflow.com/a/19356354/353985
I came across the same issue today. I have search for solution the whole. The only reliable solution that worked for me is through revoke as explained here
I stored access_token in session which is needed during revoke
Below is my code you may find it useful
function logout() {
var access_token = $('#<%=accessTok.ClientID %>').val();
var provider = $('#<%=provider.ClientID %>').val();
if (access_token && provider) {
if (provider == 'GPLUS') {
var revokeUrl = 'https://accounts.google.com/o/oauth2/revoke?token=' +
access_token;
// Perform an asynchronous GET request.
$.ajax({
type: 'GET',
url: revokeUrl,
async: false,
contentType: "application/json",
dataType: 'jsonp',
success: function (nullResponse) {
// Do something now that user is disconnected
// The response is always undefined.
},
error: function (e) {
// Handle the error
// console.log(e);
// You could point users to manually disconnect if unsuccessful
// https://plus.google.com/apps
}
});
}
else if (provider == 'FB') {
FB.getLoginStatus(function (response) {
if (response.status === 'connected') {
FB.logout();
}
});
}
} else {
}
}

Resources