Identity service 4 auto token refresh - identityserver4

I am using identity server 4 with the oidc-client library in an angular 2 web page. The login, logout calls and api calls are all working correctly without any issues. I have recently started trying to get the token auto refresh functionality to work in the web page also. I see that web page defined in silent-redirect is getting created in the iFrame and I see calls to the id4 service but always get a timeout error on the iframe and no new token received.
Any help or suggestions on what I am missing or doing wrong would be greatly appreciated. I have included relevant client side code below.
I do see the token expiring event being triggered.
thanks a lot in advance.
angular2 service
import { UserManager, Log, MetadataService, User, WebStorageStateStore } from 'oidc-client';
export const settings: any = {
authority: 'http://10.3.30.215:8885',
client_id: 'tps',
redirect_uri: 'http://10.3.30.215:4201/auth.html',
response_type: 'id_token token',
automaticSilentRenew: true,
monitorSession: true,
scope: 'openid scope1 scope2 offline_access',
post_logout_redirect_uri: 'http://10.3.30.215:4201',
silent_redirect_uri: 'http://10.3.30.215:4201/silent_renew.html',
loadUserInfo: true
};
public mgr: UserManager = new UserManager(settings);
constructor(private http: Http) {
Log.logger = console;
Log.level = Log.DEBUG;
}
login() {
this.mgr.clearStaleState().then(() => {
this.mgr.signinRedirect();
});
}
silent_renew.html:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
<script src="oidc-client.js"></script>
<script>
new Oidc.UserManager().signinSilentCallback();
</script>
</body>
</html>

Assuming your site is secure with https and you own the frontend, you could possibly do a resource owner password grant implementation with refresh tokens and use angular's built in auth features.
This site has some good info on how to implement an http interceptor. https://ryanchenkie.com/angular-authentication-using-the-http-client-and-http-interceptors.
Your use case might not allow this approach, but it is an option. Hope that helps.

Related

AngularJS - IdentityServer4.Quickstart.UI - 'AuthenticationProperties' is an ambiguous reference

I'm implementing an AngularJS app that will use IdentityServer4 for Authorization.
I have a stand alone Angular app within a .Net Core 2.0 app that calls the api controller in the .net core app. if I browse to http://localhost:5050/.well-known/openid-configuration I am getting the json returned.
I have used this example as a basis for my auth service:
function authService() {
var config = {
authority: "http://localhost:5050",
client_id: "js",
redirect_uri: "http://localhost:5050/LocalizationAdmin/callback.html",
response_type: "id_token token",
scope: "openid profile api1",
post_logout_redirect_uri: "http://localhost:5050/LocalizationAdmin/index.html"
};
var mgr = new Oidc.UserManager(config);
mgr.getUser().then(function (user) {
if (user) {
log("User logged in", user.profile);
} else {
log("User not logged in");
}
});
var service = {
login: login,
logout: logout,
};
return service;
function login() {
mgr.signinRedirect();
}
In callback.html I have added:
<body>
<script src="scripts/oidc-client.js"></script>
<script>
new Oidc.UserManager().signinRedirectCallback().then(function () {
window.location = "index.html";
}).catch(function (e) {
console.error(e);
});
</script>
</body>
It is trying to redirect to:
http://localhost:5050/account/login?returnUrl=%2Fconnect%2Fauthorize%2Fcallback%3Fclient_id%3Djs%26redirect_uri%3Dhttp%253A%252F%252Flocalhost%253A5050%252FLocalizationAdmin%252Fcallback.html%26response_type%3Did_token%2520token%26scope%3Dopenid%2520profile%2520api1%26state%3Dd526351a26f74202badb7685022a6549%26nonce%3D6c858921378645ca8fcad973eb26cc72
However I just want it to redirect to the IdentityServer4 login screen. How can I achieve this? Any help appreciated.
Edit:
I have added the UI templates from here:
https://github.com/IdentityServer/IdentityServer4.Quickstart.UI
But I am getting a number of errors, could this be because I am using .Net Core 2.0 version: assemblyref://IdentityServer4 (2.0.0-rc1-update1)
Error CS0104 'AuthenticationProperties' is an ambiguous reference between 'Microsoft.AspNetCore.Authentication.AuthenticationProperties' and 'Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties'
Demo Project showing issue added to github here.
I have no idea how stable this is but I just used the powershell command pointing to the dev branch and it seems to be working.
iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/IdentityServer/IdentityServer4.Quickstart.UI/dev/get.ps1'))
you might want to look instead at the quickstarts relating to 2.0.0-rc1-update1 which can be found here on the dev branch:
https://github.com/IdentityServer/IdentityServer4/tree/dev/docs/quickstarts
as they have been also updated.

CSRF token mismatch in Laravel / Angular app

In an app which mixes Laravel and Angular, I have this persistent CSRF token mismatch error coming up when calling a route from an Angular service. This is more or less how it's set up:
ROUTES
Route::group(['middleware' => ['web'] ], function () {
// non-auth routes (e.g. signup, login) ...
Route::group(['middleware' => 'auth'], function() {
Route::get('w/{ignore?}', function () { return view('writer.index');})
->where('ignore', '.*');
Route::match(['get', 'post'], 'doc/open', 'Controller#openItem');
});
});
The writer.index view shows up fine without token error (the user has been authenticated).
The VIEW includes:
<meta name="csrf-token" content="{{ csrf_token() }}" />
and
<script>
$(function(){
$.ajaxPrefilter(function(options, originalOptions, xhr) {
var token = $('meta[name="csrf-token"]').attr('content');
if (token) {
return xhr.setRequestHeader('X-CSRF-TOKEN', token);
}
});
});
</script>
From Angular, a service is producing a request to the doc/open route over $http.post which returns the token mismatch error.
I checked the headers and the $http.post did in fact send over a value for X-XSRF-TOKEN. However, this header value does not match the XSRF-TOKEN value in the cookie. If that's the mismatch, why is it occurring?
In laravel 5.3, this is made simple with Passport package. I was having the same trouble with Angular resource requests. I fixed it by installing Passport via composer and adding the following lines to
app\Http\Kernel.php
protected $middlewareGroups = [
'web' => [
\stix\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\stix\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
],
Now laravel will automatically include X-CRSF-TOKEN for every request made within the web middleware group. Hope this helps.

Auth0 Authorization throws Unauthorized

I try to implement Auth0 in my Single-Page-App using angular.
here are the scripts i loaded for auth0:
<!-- Auth0's lock widget library -->
<script type="text/javascript" src="https://cdn.auth0.com/js/lock-9.0.js"></script>
<!-- Angular cookie wrapper library for client cookies -->
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular-cookies.js"></script>
<!-- Auth0's Angular SDK Library -->
<script type="text/javascript" src="https://cdn.auth0.com/w2/auth0-angular-4.js"></script>
<!-- Angular wrapper for localStorage and sessionStorage. Defaults to ng-cookies if not available -->
<script src="http://cdn.rawgit.com/auth0/angular-storage/master/dist/angular-storage.js" type="text/javascript"> </script>
<!-- Angular wrapper library for JWT-->
<script src="http://cdn.rawgit.com/auth0/angular-jwt/master/dist/angular-jwt.js" type="text/javascript"> </script>
But the Token has an invalid Signature and is always unauthorized when i try the api call for the user information here : Api to get user data
the token is stored in the local storage in the following eventhandler
authProvider.on('loginSuccess', ['$location', 'profilePromise', 'idToken', 'store',
function($location, profilePromise, idToken, store) {
profilePromise.then(function(profile) {
store.set('user', profile.email);
store.set('token', idToken);
});
$location.path('/');
}]);
The debugger from jwt.io always says invalid Signature when i try to decode my jwt even with my client secret.
Did i do something wrong ? Why is my token Signature invalid ? Why is my token unauthorized? Does the Api page even work on actual data ?
EDIT:
I found out that i used the wrong api call i used the one for auth0-access-tokens instead for JWTs.
But still the JWT Debugger shows invalid Signature
If you are using the idToken, you should be calling the /tokeninfo endpoint. Be sure to follow the required format: it should be a POST, with content type json containing this payload:
{
"id_token" : "[the token]"
}
However, you probably don't want to do any of this. The call to /tokeninfo will get you the user profile, but the SDK already does that for you after authenticating the user. The same user profile is available in the callback from profilePromise here:
authProvider.on('loginSuccess', ['$location', 'profilePromise', 'idToken', 'store',
function($location, profilePromise, idToken, store) {
profilePromise.then(function(profile) {
// 'profile' contains the same information that
// you would get calling '/tokeinfo' or '/userinfo'
// so consider storing the whole 'profile' instead
// of just 'profile.email'
store.set('user', profile.email);
store.set('token', idToken);
});
$location.path('/');
}]);

Customizing auth0-lock with auth0-angular

I'm experimenting with auth0-lock from www.auth0.com and AngularJS.
I was able to pass some parameters as in SDK: https://auth0.com/docs/libraries/lock/customization#connections-array-
auth.signin({
disableSignupAction: true,
socialBigButtons: true,
//these lines don't work
//no changes (username, password appears)
//connection: 'windows-live'
//this line returns 'No valid connection found'
//connections: 'windows-live'
//this line returns 'No valid connection found'
//connections: ['windows-live']
//this line doesn't change anything
//connection: ['windows-live']
}
but I'm not able to pass connections (non angular version from SDK)
lock.show({
connections: ['twitter', 'facebook', 'linkedin']
});
Both parameters connection or connections don't work.
The problem that I have is that if I don't specify connection (connections) username and password will appear. According to SDK I need to hardcode it, to hide these fields.
As SDK is written for JS not AngularJS, it seems that I have an issue with passing array, or parameter.
Any ideas?
You want to send an array with the connections parameter:
connections: ['windows-live']
Instead of a string like you have:
connections: 'windows-live'
Have you enabled the Social in the dashboard?
The documentation seems to be so-so on this. If you use //cdn.auth0.com/js/lock-8.js it should work out of the box as seen in previous response and following the examples https://github.com/auth0/auth0-angular/tree/master/examples.
I wrestled with connections: & connection for several hours trying to get some custom connections working.
For you as a base you could enable the various providers in the Auth0 social providers, make sure they toggle green. Start as simple as possible and expand from there.
auth.signin({
scope: 'openid name email'
index.html
<script type="text/javascript" src="//cdn.auth0.com/js/lock-8.js"></script>
<!--script src="//cdn.auth0.com/w2/auth0-6.7.js"></script-->
<script type="text/javascript" src="https://cdn.auth0.com/w2/auth0-angular-4.js"></script>
<script src="./lib/a0-angular-storage/dist/angular-storage.js"></script>
<script src="./lib/angular-jwt/dist/angular-jwt.js"></script>
login ctrl
.controller('LoginCtrl',
function onLoginSuccess(profile, token) {
console.log("login ok");
}
function onLoginFailed() {
console.log("failed login");
}
auth.signin({
scope: 'openid name email'
}, onLoginSuccess, onLoginFailed);
}
config block
.config(function($stateProvider, $urlRouterProvider, authProvider,
jwtInterceptorProvider, $httpProvider) {
authProvider.init({
domain: '',
clientID: '',
loginState: 'login',
loginUrl: true
});
authProvider.on('loginSuccess', function($location, profilePromise, idToken, store) {
profilePromise.then(function(profile) {
console.log(JSON.stringify(profile));

Authentication with AngularJS, session management and security issues with REST Api WS

I started developing a web-app with angularJS and I'm not sure that everything is right secured (client and server side).
Security is based on a single login page, if credentials are checked ok, my server sends back an unique token with custom time-validity. All other REST api are accessible through this token.
The application (client) browse to my entry-point ex: https://www.example.com/home.html user insert credentials and receive back a unique token. This unique token is stored in the server database with AES or other secure techniques, it is not stored in clear format.
From now on, my AngluarJS app will use this token to authenticate to all REST Api exposed.
I'm thinking on temporary store the token in a custom http cookie; basically, when the server verifies the credentials, it sends back a new cookie Ex.
app-token : AIXOLQRYIlWTXOLQRYI3XOLQXOLQRYIRYIFD0T
The cookie has the secure and HTTP Only flags set on.
Http protocol directly manage the new cookie and store it. Successive requests will presents the cookie with the new parameter, without the need to manage it and store it with javascript; at every request, server invalidates the token and generates a new one and sends it back to the client --> prevent replay-attacks with a single token.
When the client receives an HTTP status 401 unauthorized response from any REST Api, the angular controller clean all the cookies and redirect the user to the login page.
Should I have to consider other aspects? Is it better to store the token inside a new cookie or in localStorage?
Any tips on how to generate a unique strong token?
Edit (improvements):
I decided to use HMAC-SHA256 as session token generator, with 20 minutes validity. I generate a random 32byte GUID, attach a timestamp and compute the HASH-SHA256 by providing a 40 bytes key. It's quite impossible to obtain collisions since the token validity is quite minimal.
Cookie will have domain and path attributes to increase security.
No multi-logins are permitted.
If you talk to the server via https, you don't have a problem with replay attacks.
My suggestion would be to leverage your server's security technology. For example, JavaEE has an out-of-the-box login mechanism, declarative role-based protection of resources (your REST endpoints) etc. These are all managed with a set of cookies and you don't have to care about storage and expiration. Check out what your server/framework already gives you.
If you plan to expose your API to a broader audience (not specifically to the browser-based UI that you serve) or other types of clients (e.g. mobile app), consider adopting OAuth.
Off the top of my head, Angular has the following security features (will add more as they pop-out):
CSRF/XSRF attacks
Angular supports an out of the box mechanism for CSRF protection. Check out $http docs. Server-side support is needed.
Content Security Policy
Angular has a mode of expression evaluation that is compatible with more strict JavaScript runtimes that are enforced when CSP is enabled. Check out ng-csp docs.
Strict Contextual Escaping
Use Angular's new $sce feature (1.2+) to harden you UI against XSS attacks etc. It's a bit less convenient but more secure. Check out the docs here.
This is client side security which you can implement in regular Angular versions.
I have tried and tested this.
(Please find my article here:- https://www.intellewings.com/post/authorizationonangularroutes )
In addition to client side route security, you need to secure access at server side also.
Client side security helps in avoiding extra round trip to server. However, if someone tricks the browser , then server server side security should be able to reject unauthorized access.
Hope this helps!
Step 1: Define Global variables in app-module
-define roles for the application
var roles = {
superUser: 0,
admin: 1,
user: 2
};
-Define route For Unauthorized Access for the application
var routeForUnauthorizedAccess = '/SomeAngularRouteForUnauthorizedAccess';
Step 2: Define the service for authorization
appModule.factory('authorizationService', function ($resource, $q, $rootScope, $location) {
return {
// We would cache the permission for the session, to avoid roundtrip to server for subsequent requests
permissionModel: { permission: {}, isPermissionLoaded: false },
permissionCheck: function (roleCollection) {
// we will return a promise .
var deferred = $q.defer();
//this is just to keep a pointer to parent scope from within promise scope.
var parentPointer = this;
//Checking if permisison object(list of roles for logged in user) is already filled from service
if (this.permissionModel.isPermissionLoaded) {
//Check if the current user has required role to access the route
this.getPermission(this.permissionModel, roleCollection, deferred);
} else {
//if permission is not obtained yet, we will get it from server.
// 'api/permissionService' is the path of server web service , used for this example.
$resource('/api/permissionService').get().$promise.then(function (response) {
//when server service responds then we will fill the permission object
parentPointer.permissionModel.permission = response;
//Indicator is set to true that permission object is filled and can be re-used for subsequent route request for the session of the user
parentPointer.permissionModel.isPermissionLoaded = true;
//Check if the current user has required role to access the route
parentPointer.getPermission(parentPointer.permissionModel, roleCollection, deferred);
}
);
}
return deferred.promise;
},
//Method to check if the current user has required role to access the route
//'permissionModel' has permission information obtained from server for current user
//'roleCollection' is the list of roles which are authorized to access route
//'deferred' is the object through which we shall resolve promise
getPermission: function (permissionModel, roleCollection, deferred) {
var ifPermissionPassed = false;
angular.forEach(roleCollection, function (role) {
switch (role) {
case roles.superUser:
if (permissionModel.permission.isSuperUser) {
ifPermissionPassed = true;
}
break;
case roles.admin:
if (permissionModel.permission.isAdministrator) {
ifPermissionPassed = true;
}
break;
case roles.user:
if (permissionModel.permission.isUser) {
ifPermissionPassed = true;
}
break;
default:
ifPermissionPassed = false;
}
});
if (!ifPermissionPassed) {
//If user does not have required access, we will route the user to unauthorized access page
$location.path(routeForUnauthorizedAccess);
//As there could be some delay when location change event happens, we will keep a watch on $locationChangeSuccess event
// and would resolve promise when this event occurs.
$rootScope.$on('$locationChangeSuccess', function (next, current) {
deferred.resolve();
});
} else {
deferred.resolve();
}
}
};
});
Step 3: Use security in routing: Lets use use all our hardword done so far, to secure the routes
var appModule = angular.module("appModule", ['ngRoute', 'ngResource'])
.config(function ($routeProvider, $locationProvider) {
$routeProvider
.when('/superUserSpecificRoute', {
templateUrl: '/templates/superUser.html',//path of the view/template of route
caseInsensitiveMatch: true,
controller: 'superUserController',//angular controller which would be used for the route
resolve: {//Here we would use all the hardwork we have done above and make call to the authorization Service
//resolve is a great feature in angular, which ensures that a route controller(in this case superUserController ) is invoked for a route only after the promises mentioned under it are resolved.
permission: function(authorizationService, $route) {
return authorizationService.permissionCheck([roles.superUser]);
},
}
})
.when('/userSpecificRoute', {
templateUrl: '/templates/user.html',
caseInsensitiveMatch: true,
controller: 'userController',
resolve: {
permission: function (authorizationService, $route) {
return authorizationService.permissionCheck([roles.user]);
},
}
})
.when('/adminSpecificRoute', {
templateUrl: '/templates/admin.html',
caseInsensitiveMatch: true,
controller: 'adminController',
resolve: {
permission: function(authorizationService, $route) {
return authorizationService.permissionCheck([roles.admin]);
},
}
})
.when('/adminSuperUserSpecificRoute', {
templateUrl: '/templates/adminSuperUser.html',
caseInsensitiveMatch: true,
controller: 'adminSuperUserController',
resolve: {
permission: function(authorizationService, $route) {
return authorizationService.permissionCheck([roles.admin,roles.superUser]);
},
}
})
});
app/js/app.js
-------------
'use strict';
// Declare app level module which depends on filters, and services
var app= angular.module('myApp', ['ngRoute']);
app.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/login', {templateUrl: 'partials/login.html', controller: 'loginCtrl'});
$routeProvider.when('/home', {templateUrl: 'partials/home.html', controller: 'homeCtrl'});
$routeProvider.otherwise({redirectTo: '/login'});
}]);
app.run(function($rootScope, $location, loginService){
var routespermission=['/home']; //route that require login
$rootScope.$on('$routeChangeStart', function(){
if( routespermission.indexOf($location.path()) !=-1)
{
var connected=loginService.islogged();
connected.then(function(msg){
if(!msg.data) $location.path('/login');
});
}
});
});
app/js/controller/loginCtrl.js
-------------------------------
'use strict';
app.controller('loginCtrl', ['$scope','loginService', function ($scope,loginService) {
$scope.msgtxt='';
$scope.login=function(data){
loginService.login(data,$scope); //call login service
};
}]);
app/js/directives/loginDrc.js
-----------------------------
'use strict';
app.directive('loginDirective',function(){
return{
templateUrl:'partials/tpl/login.tpl.html'
}
});
app/js/services/sessionService.js
---------------------------------
'use strict';
app.factory('sessionService', ['$http', function($http){
return{
set:function(key,value){
return sessionStorage.setItem(key,value);
},
get:function(key){
return sessionStorage.getItem(key);
},
destroy:function(key){
$http.post('data/destroy_session.php');
return sessionStorage.removeItem(key);
}
};
}])
app/js/services/loginService
----------------------------
'use strict';
app.factory('loginService',function($http, $location, sessionService){
return{
login:function(data,scope){
var $promise=$http.post('data/user.php',data); //send data to user.php
$promise.then(function(msg){
var uid=msg.data;
if(uid){
//scope.msgtxt='Correct information';
sessionService.set('uid',uid);
$location.path('/home');
}
else {
scope.msgtxt='incorrect information';
$location.path('/login');
}
});
},
logout:function(){
sessionService.destroy('uid');
$location.path('/login');
},
islogged:function(){
var $checkSessionServer=$http.post('data/check_session.php');
return $checkSessionServer;
/*
if(sessionService.get('user')) return true;
else return false;
*/
}
}
});
index.html
----------
<!doctype html>
<html lang="en" ng-app="myApp">
<head>
<meta charset="utf-8">
<title>My AngularJS App</title>
<link rel="stylesheet" href="css/app.css"/>
</head>
<body>
<div ng-view></div>
<!-- In production use:
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
-->
<script src="lib/angular/angular.js"></script>
<script src="lib/angular/angular-route.js"></script>
<script src="js/app.js"></script>
<script src="js/directives/loginDrc.js"></script>
<script src="js/controllers/loginCtrl.js"></script>
<script src="js/controllers/homeCtrl.js"></script>
<script src="js/services/loginService.js"></script>
<script src="js/services/sessionService.js"></script>
</body>
</html>
First, there is no short or only one answer to what you have asked. In addition to what has already been answered, let me try to add something more. At enterprise level , there are four major components ,
UI
User Authentication Server - Here you validate user credentials and generate necessary cookies for user to move forward on UI. If this step fails, user gets stopped right there. This server has nothing to do with API token generation & you need this for non - API based systems too.Google Authentication is one example.
Extension:Siteminder Authentication
SiteMinder Cookies, their Usage, Contents and Security
Building a Java authentication server for Chatkit
API Token Server - This server generates API tokens based on cookies generated on step # 2 i.e. you send cookies to server and get a token
APIs - You use token generated in step # 3 to make API calls.
Its better that you deploy & manage these four components independently for better scale . e.g. in this article, they have mixed up authentication & token generation in single end point & thats not good - Microservices with Spring Boot — Authentication with JWT (Part 3)
By your write up, it looks that you have written component two & three on your own - usually folks utilize some ready made tools for this like CA SiteMinder - How CA Siteminder works – Basics
Any tips on how to generate a unique strong token?
I would suggest that you go via standardized way for better maintainability & security i.e. you choose JWT format. JSON Web Token (JWT) Authentication Scheme
Your token will be signed & encrypted so You would also need an encryption key server & a mechanism to rotate these keys at regular intervals.
JSON Web Tokens - How to securely store the key?
What is the difference between JWT and encrypting some json manually with AES?
CA person has attached a detailed pdf guide on this community portal - that will help you to understand the overall flow.
Sample Code / App to use of REST JWT token API
Your API code will need to fetch the encryption key and decrypt & decode the token to authenticate token. If token is tampered or missing, you need to flag it as such. There are libraries available for this.
Is it better to store the token inside a new cookie or in
localStorage?
local Storage if UI & API are on different domains & Cookies if on same domain.
Should JWT be stored in localStorage or cookie?
Cross-Domain Cookies
Security of an application is also dependent on deployment model and that part you haven't specified in your question. Sometimes , developers might leave as simple flaws in their code as SQL Injection :)
What if JWT is stolen?

Resources