Sending authenticated requests to GitHub API from authorized Auth0 app user - angularjs

I am having a very difficult time getting authenticated API requests to GitHub to work. I have created an authorized application in GitHub and connected it to my Auth0 account. I have no problems getting a user signed in using their GitHub account but once they are signed in I cannot make authenticated requests to the GitHub API (I am trying to set a GitHub webhook in one of the user's GitHub repos). All my requests are rejected for having incorrect credentials.
I have the JWT issued by Auth0 being sent along in each request to the GitHub API endpoint but it appears as though this is not sufficient. The Auth0 profile that comes back from my user seems to have an access_token in it, but sending this along does not work either.
Here is what my Auth0 login code looks like (using the Angular API):
angular.module('myApp').controller('LoginCtrl', ['$scope', '$http', 'auth', 'store', '$location',
function ($scope, $http, auth, store, $location) {
$scope.login = function () {
auth.signin({
authParams: {
responseType: 'token' // I think this is the default but just in case
}
}, function (profile, token) {
// Success callback
store.set('profile', profile);
store.set('token', token);
$location.path('/');
}, function () {
// Error callback
console.debug("error logging in");
});
};
}]);
This works fine. They authorize the GitHub application tied to my organization's Auth0 account with its requested permissions without issue and land back in my application and I then have access to an Auth0 profile tied to their GitHub account, but then if I try and make an authenticated request to the GitHub API on their behalf:
var username = auth.nickname;
var repo = "some_user.github.io"; // todo: get repo from setup process
var url = "https://api.github.com/repos/" + username + "/" + repo + "/hooks/";
var conf = {
name: "web",
active: true,
config: {
"url": "https://webtask.it.auth0.com/api/run/wt-my-container_com-0/echo?webtask_no_cache=1",
"content_type": "json"
}
};
$http.post(url, conf).success(function(data, status) {
console.log("post successful:");
console.log(status);
console.log(data);
});
... GitHub rejects the request, either saying the request resource doesn't exist (to prevent private data leakage) or that I supplied bad credentials, depending on different variables (if I try supplying the "access_token" field provided in their Auth0 profile as a query param or supply my Auth0 application's client secret, etc).
I have scoured the documentation of both Auth0 and GitHub trying to figure out what the correct procedure is (for example, do I need to implement the whole OAuth2 token flow myself? it seems like Auth0 should be doing that for me) but nothing I have tried so far works, and nothing on Google has pointed me in the right direction. I have tried a number of other methods of doing this without success but I don't want to make this post too much longer. Any help would be greatly appreciated.

I figured it out. There were two problems: one, a trailing slash had crept in on the end of my API call to the GitHub endpoint, which evidently breaks something and causes GitHub to reject the request, and second, I had set things up to send along the Authorization header with every request as per the Auth0 guide here: https://auth0.com/docs/client-platforms/angularjs, specifically this part:
myApp.config(function (authProvider, $routeProvider, $httpProvider, jwtInterceptorProvider) {
// ...
// We're annotating this function so that the `store` is injected correctly when this file is minified
jwtInterceptorProvider.tokenGetter = ['store', function(store) {
// Return the saved token
return store.get('token');
}];
$httpProvider.interceptors.push('jwtInterceptor');
// ...
});
But GitHub does not like that since it does not contain the token it is expecting and will reject the request if it sees it. Once I removed the trailing slash and removed the above code, everything started working as expected.

Look at this gitHub page. It is something like this with angular:
//'common' will add the headder to every request.
$httpProvider.defaults.headers.common["Authorization"] = token YOUR_TOKEN;

Related

Backand.com - Random error 404 during REST API

for one web app I'm using Backand.com as MBAAS .
I created a data model and linked all tables.
If I query the default REST APIs there is no problem at all, however if I try to call custom action or custom query the server randomly answer with error 404 not found....
For example I've this custom action named: getCommentsByEvent
'use strict';
function backandCallback(userInput, dbRow, parameters, userProfile) {
// write your code here
return $http ({
method: 'GET',
url: CONSTS.apiUrl + '/1/query/data/getCommentsByEvent',
params: {
parameters: {
event_id: {{event_id}}
}
},
headers: {"Authorization": userProfile.token}
});
}
From the web App I use this code to call it via REST
service.commentsByEvent = function(p, s, r) {
$http({
method: 'GET',
url: Backand.getApiUrl() + '/1/objects/action/comments/?name=getCommentsByEvent',
params: {
parameters: {
event_id: p.eventId
}
}
}).then(s, r);
};
The custom action is written correctly because if I try to call it directly from test page of Backand.com it works without any problem, however if I try from my web app 80% of the time it does not work, 20% it works
This is the error:
Object {data: "Action not found, or is not on demand", status: 404, config: Object, statusText: "Not Found"}
Just to know:
- the action does not have security profile or role or others;
- before to call this action web app call the signin API to get the token
- the action does not need the auth token because is public
To call the signin API I made this service:
service.signin = function(email, password) {
//set the app name of Backand. In your app copy this to .config section with hard coded app name
Backand.setAppName($rootScope.appName);
//call Backand for sign in
return Backand.signin(email, password);
};
service.signout = function() {
return Backand.signout();
};
Any idea?
Thanks
Michele
Fixed!
It was a very dummy issue related with the "App name" created on Backand.com website....
On Backand.com I created an app with all name in uppercase like "MYAPP" so when I configured the SDK on Angular I wrote
Backand.setAppName("MYAPP);
However Backand.com display the same name on the website, but during the configuration it require a LOWERCASE ONLY name! Nobody, even in customer care, knew this requirements so it was very difficult to understand how to fix... Yesterday I did a try in this way and it worked...
Backand.setAppName("myapp");
To sum up, if you have an error 404 and the URL is correct, there is no authentication issue check if the APP NAME is ONLY LOWERCASE.

Node API - How to link Facebook login to Angular front end?

Rewriting this question to be clearer.
I've used passport-facebook to handle login with facebook on my site.
My front end is in Angular so I know now need to understand whats the correct way of calling that api route. I already have several calls using Angular's $http service - however as this login with facebook actually re-routes the facebook page can i still use the usual:
self.loginFacebook = function )() {
var deferred = $q.defer();
var theReq = {
method: 'GET',
url: API + '/login/facebook'
};
$http(theReq)
.then(function(data){
deferred.resolve(data);
})
return deferred.promise;
}
or is it perfectly ok/secure/correct procedure to directly hit that URL in a window location:
self.loginFacebook = function (){
$window.location.href = API + '/login/facebook';
}
Furthermore, from this how do I then send a token back from the API? I can't seem to modify the callback function to do that?
router.get('/login/facebook/callback',
passport.authenticate('facebook', {
successRedirect : 'http://localhost:3000/#/',
failureRedirect : 'http://localhost:3000/#/login'
})
);
Thanks.
I was stacked on the same problem.
First part:
I allow in backend using cors and in frontend i use $httpProvider, like this:
angular.module('core', [
'ui.router',
'user'
]).config(config);
function config($httpProvider) {
$httpProvider.defaults.useXDomain = true;
$httpProvider.defaults.headers.common['X-Requested-With'];
$httpProvider.defaults.headers.common["X-Requested-With"] = 'XMLHttpRequest';
};
The second part:
<span class="fa fa-facebook"></span> Login with facebook
This call my auth/facebook route that use passport to redirect to facebook page allowing a user to be authenticated.
If the user grant access, the callback /api/auth/facebook/callback is called and the facebook.strategy save the user with the profile data.
After saving the user, i create a special token with facebook token, id and email. This info is used to validate every time the user access to private states in the front.
My routes are something like this:
router.get('/facebook', passport.authenticate('facebook',
{ session: false, scope : 'email' }));
// handle the callback after facebook has authenticated the user
router.get('/facebook/callback',
passport.authenticate('facebook',
{session: false, failureRedirect: '/error' }),
function(req, res, next) {
var token = jwt.encode(req.user.facebook, config.secret);
res.redirect("/fb/"+token);
});
In frontend i catch the /fb/:token using a state and assign the token to my local storage, then every time the user go to a private section, the token is sent to backend and validate, if the validation pass, then the validate function return the token with the decoded data.
The only bad thing is that i don't know how to redirect to the previous state that was when the user click on login with facebook.
Also, i don't know how you are using the callback, but you need to have domain name to allow the redirect from facebook. I have created a server droplet in digitalocean to test this facebook strategy.
In the strategy you have to put the real domain in the callback function, like this:
callbackURL: "http://yourdomain.com/api/auth/facebook/callback"
In the same object where you put the secretId and clientSecret. Then, in your application in facebook developers you have to allow this domain.
Sorry for my english, i hope this info help you.
Depending on your front-end, you will need some logic that actually makes that call to your node/express API. Your HTML element could look like
<a class='btn' href='login/facebook'>Login</a>
Clicking on this element will make a call to your Express router using the endpoint of /login/facebook. Simple at that.

Handling session/cookie with Sails.JS and AngularJS

I'm doing a simple SPA where I am using Sails.JS for a REST API and AngularJS for my frontend.
I'm currently having some struggles with figuring out how I should handle the sessions when combining these two.
Feel free to give me some pointers if I'm going about this the wrong way.
--
Here is part of my login function. When a successfull login happens I return the user object along with a session to my client.
User.js
if(user) {
bcrypt.compare(userObj.password, user.encryptedPassword, function(err, match) {
if(err) {
res.json({rspMessage: 'Server error'}, 500);
}
if(match) {
req.session.user = user;
res.json(req.session.user); // return user data and session.
/* This returns something like this
{ cookie:
{ path: '/',
_expires: null,
originalMaxAge: null,
httpOnly: true },
user: {
username: 'admin',
id: '549f2ad213c64d3b2f3b9777'}
}
*/
}
});
}
loginService
Here is my loginService which doesn't really do much right now. I figured this is the place to keep track of the session. I'm just not sure how to go about this... There aren't many tutorials about combining Sails + AngularJS.
MyApp.factory('loginService', ['$cookieStore', '$http', '$rootScope', function($cookieStore, $http, $rootScope){
var _user = {};
return {
login: function(credentials) {
return $http.post('/user/login', credentials)
.then(function(result) {
return result.data;
});
}
}
}])
I want to check the session against my backend somehow and see if its valid or if it has expired. If the session is still valid, the user will be kept logged in even if the user closes his browser/refresh.
Suggestions, links.. anything helpful is appreciated.
Here's some tips I can give you :
Since Sails v0.10, you can use custom responses (doc page) which is a better practice than using
res.status(...);
res.json(...);
The session cookie you are creating with Sails is saved server-side. Maybe you can create a url (e.g. GET /me) to know if this session is still valid. Your Angular app would make a request to this url each time the page is loaded (in a run block I would suggest) to know if the user is still logged in server-side.
Do not hesitate if you need more precision.

Login by facebook in angular app with loopback backend

I'm making an angular application with strongloop loopback backend.
Also I integrating a third party login by facebook using loopback-passport module.
everything was fine in loopback-example-passport and everything is fine in my app right before the moment of redirecting to my app. User and Access-token created.
the code:
app.get('/auth/login', ensureLoggedIn('/#login'), function(req, res, next) {
console.log('LOOGED IN!!');
console.log(req.user);
res.redirect('/#auth/login');
});
works fine. But i can't understand. how to give authenticated state to my angular application.
i tried to make a controller to route '/#auth/login':
.controller('AuthCalbackCtrl', function($scope, $cookies, $location, AppAuth, $http, User, LoopBackAuth) {
//analogue of User.login responce interceptor
LoopBackAuth.currentUserId = $cookies['userId'] || null;
LoopBackAuth.accessTokenId = $cookies['access-token'] || '';
LoopBackAuth.rememberMe = false;
LoopBackAuth.save();
//asking for currentUser
User.getCurrent(function(user) {
console.log('ser.getCurrent ', user);
});
$location.path('/');
})
This code makes a request GET /api/users/2 but receives 401 error.
If I tweak the file /loopback/lob/models/user.js setting permission:
principalType: ACL.ROLE,
// principalId: Role.OWNER,
principalId: Role.EVERYONE,
permission: ACL.ALLOW,
property: "findById"
Then the request GET /api/users/2 receives 200 and everything ok.
I'm a little confused. I can`t understand how to make my angular app authenticate to loopback, although i know access-token and userId
Have anybody any ideas how to do it?
Here is a valid code.
app.get('/auth/login', function(req, res, next) {
//workaround for loopback-password
//without this angular-loopback would make incorrect authorization header
res.cookie('access-token', req.signedCookies['access-token']);
res.cookie('userId', req.user.id);
res.redirect('/#auth/login');
});
The problem is that loopback-passport signs cookie:
res.cookie('access-token', info.accessToken.id, { signed: true,
maxAge: info.accessToken.ttl });
In string it looks something like the following "s:.eBvo8bpo9Q9wnNrPjjlG%2FAcYqWkxEgNFqn%2FO54rdGwY"
But loopback-angular just copies the access-token to header.authorization, so we need to put there plain cookie.

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