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

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

Related

Service Worker w offline.html Backup Page

I can't get the offline.html page to display. I keep getting the The FetchEvent for "https://my-domain.com" resulted in a network error response: a redirected response was used for a request whose redirect mode is not "follow".
Here's the snippet of my service-worker.js which should return the offline.html when the network is unavailable.
self.addEventListener('fetch', function(event) {
if (event.request.mode === 'navigate' || (event.request.method === 'GET' && event.request.headers.get('accept').includes('text/html'))) {
if(event.request.url.includes("my-domain.com")){
console.log(event.request);
event.respondWith(
caches.match(event.request).then(function(resp) {
return resp || fetch(event.request).then(function(response) {
let responseClone = response.clone();
caches.open(CACHE_NAME).then(function(cache) {
cache.put(event.request, responseClone);
});
return response;
});
}).catch(function() {
return caches.match("/offline.html");
})
);
}
}
});
Below is the console.log of my network request (page refresh when offline)
Request {method: "GET", url: "https://my-domain.com", headers: Headers, destination: "unknown", referrer: "", …}
bodyUsed:false
cache:"no-cache"
credentials:"include"
destination:"unknown"
headers:Headers {}
integrity:""
keepalive:false
method:"GET"
mode:"navigate"
redirect:"manual"
referrer:""
referrerPolicy:"no-referrer-when-downgrade"
signal:AbortSignal {aborted: false, onabort: null}
url:"https://my-domain.com"
__proto__:Request
I got this working / found the fix. It was related to a redirected response security issue in the browser. From the Chromium Bugs Blog, Response.redirected and a new security restriction.
Solution: To avoid this failure, you have 2 options.
You can either change the install event handler to store the response generated from res.body:
self.oninstall = evt => {
evt.waitUntil(
caches.open('cache_name')
.then(cache => {
return fetch('/')
.then(response => cache.put('/', new Response(response.body));
}));
};
Or change both handlers to store the non-redirected response by setting redirect mode to ‘manual’:
self.oninstall = function (evt) {
evt.waitUntil(caches.open('cache_name').then(function (cache) {
return Promise.all(['/', '/index.html'].map(function (url) {
return fetch(new Request(url, { redirect: 'manual' })).then(function (res) {
return cache.put(url, res);
});
}));
}));
};
self.onfetch = function (evt) {
var url = new URL(evt.request.url);
if (url.pathname != '/' && url.pathname != '/index.html') return;
evt.respondWith(caches.match(evt.request, { cacheName: 'cache_name' }));
};

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.

auth0 invalid token toward Rails API

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.

OKTA - OAuthError: Illegal value for redirect_uri parameter

I am working in my Developer Account in OKTA.
I am trying to get a Very Simple SPA App to obtain a JWT From OKTA.
I login with authClient.signIn({}) and that returns a transaction.sessionToken/
With that Session Token I should be able to call authClient.token.getWithoutPrompt({}) but I can never reach that code.
I get the following error: OAuthError: Illegal value for redirect_uri parameter.
How do I get beyond this OAuth Error so I can finally get back a JWT. I have tried examples on OKTA GIT but cannot get anything to work.
function getJWT()
{
var orgUrl = 'https://MYXXXXXXX.oktapreview.com';
var keyID = "MY CLIENT ID";
var user = "MYUSER ID";
var pwd = "MY PASWORD"
var appScope = ['openid', 'email', 'profile', 'phone', 'groups'];
var authClient = new OktaAuth({url: orgUrl, clientId: keyID,});
$('#tokendiv').html('');
authClient.signIn({
username: user,
password: pwd,
redirectUri: "http://localhost:5656/test.html" //THIS IS SETUP IN MY OKTA ID
})
.then(function(transaction) {
if (transaction.status === 'SUCCESS') {
authClient.session.setCookieAndRedirect(transaction.sessionToken); // Sets a cookie on redirect
console.log(transaction.sessionToken);
$('#tokendiv').append('<h4>Session Token</h4>' + transaction.sessionToken);
/// THIS IS NEVER REACHED, I Always Get OAuthError: Illegal value for redirect_uri parameter.
authClient.token.getWithoutPrompt({
responseType: 'id_token', // or array of types
scopes: appScope,
sessionToken: transaction.sessionToken
})
.then(function(res) {
console.log("JWT: " + jwt);
$('#tokendiv').append('<h4>JWT</h4>' + res.idToken);
})
.fail(function(err) {
console.log("ERROR " + err);
$('#tokendiv').append('<h4>Error</h4>' + err);
})
} else {
throw 'We cannot handle the ' + transaction.status + ' status';
}
})
.fail(function(err) {
console.error(err);
});
}
As long as your redirectUri is included in the approved Redirect URIs in your Okta Developer organization, you should not be receiving that error.
Below I was able to successfully return an id_token running on localhost:5656.
<!-- index.html -->
<html>
<body>
<button onClick="getJWT()">Button</button>
<div id="tokendiv"></div>
</body>
</html>
<script>
function getJWT(){
var orgUrl = 'https://{{org}}.oktapreview.com';
var keyID = "{{clientId}}";
var user = "{{user}}";
var pwd = "{{password}}"
var appScope = ['openid', 'email', 'profile', 'phone', 'groups'];
var authClient = new OktaAuth(
{
url: orgUrl,
clientId: keyID,
redirectUri: "http://localhost:5656/index.html"
});
$('#tokendiv').html('');
authClient.signIn({
username: user,
password: pwd,
})
.then(function(transaction) {
if (transaction.status === 'SUCCESS') {
authClient.token.getWithoutPrompt({
responseType: 'id_token', // or array of types
scopes: appScope,
sessionToken: transaction.sessionToken
})
.then(function(res) {
$('#tokendiv').append('<h4>JWT</h4>' + res.idToken);
console.log("JWT: " + JSON.stringify(res));
})
.fail(function(err) { /* err */ })
} else { /* throw error */ }
})
.fail(function(err) { /* err */ });
}
</script>

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