Spring Security cookie arrives Angular only after first forbidden request - angularjs

I'm using Spring Security for securing my Spring Data REST webapplication with AngularJS.
My SecurityConfig is declared like this:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic().and()
.authorizeRequests()
// public ressources
.antMatchers("/index.html", "/templates/public/**", "/js/**", "/").permitAll()
.antMatchers(HttpMethod.GET, "/api/security/user").permitAll()
.antMatchers("/api/**").hasAnyRole("ADMIN", "NORMALUSER")
.anyRequest().authenticated()
.and().exceptionHandling().authenticationEntryPoint(basicAuthenticationEntryPointHandler)
.and().logout().logoutUrl("/logout").logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler()).deleteCookies("JSESSIONID").permitAll().and()
.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
I generally followed this link for reaching my goal.
Now, if I would login myself for the first time, a angular-request for $cookies.get("XSRF-TOKEN") returns undefined and every request to my database isn't blocked.
But after logging out and logging in again, it returns me the cookie and every database-request is allowed.
And this happens only every second login, so:
undefined
cookie
undefined
cookie
undefined
...
So now I hope that there's anyone who can help me without going deeper into my structure, but if it's required, I will do so.
Thanks in advance.
Edit 1: Here are the requests:
In relation to the process: I'm logging the tokens in my CokieCsrfTokenRepository().loadToken(), and there all tokens are shown..
Edit 2: My Angular Service, which I'm calling by every login:
function authenticationService($rootScope, $http, $location, $filter, $cookies){
function authenticate(credentials, callback) {
var headers = credentials ? {authorization : "Basic "
+ btoa(credentials.username + ":" + credentials.password)
} : {};
$http.get('api/security/user', {headers : headers}).success(function(data, status, get, header) {
if (data.name) {
$rootScope.authenticated = true;
$rootScope.session = data;
console.log($cookies.get("XSRF-TOKEN")) // -> returns every second login cookie
} else {
$rootScope.authenticated = false;
$rootScope.session = null;
}
console.log($rootScope.session)
callback && callback();
}).error(function() {
$rootScope.authenticated = false;
callback && callback();
});
}
return {
authenticate: function(credentials){
authenticate(credentials, function() {
if ($rootScope.authenticated == true) {
$rootScope.authenticationError = false;
if ($rootScope.session.principal.admin == true){
$location.path("/admin/manage");
} else{
$location.path("/user");
}
} else {
$rootScope.authenticationError = true;
$location.path("/login");
}
});
},
// called by refreshing browser
checkLoggedIn: function(){
authenticate();
},
logout: function(){
$http.post('/logout', {})["finally"](function() {
$rootScope.authenticated = false;
$rootScope.session = null;
$location.path("/login");
});
}
};
}
Edit 3: I mentioned now, that if the cookie is undefined, the method loadToken() from this link is called only after logging out and refreshing the browser (first login). Then the token is shown and I'm logged in, again. But after every second try it still works great..
Edit 4: So no I recognized, that after the first forbidden POST-request (in Edit3 it's the /logout) the token arrives my template. After refreshing the browser, all my requests are allowed, because now the cookie will be send for every request. But how to fix this?

My solution:
//WebSecurityConfigurerAdapter:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
....
.addFilterAfter(new CsrfCustomFilter(), CsrfFilter.class).and()
.csrf().csrfTokenRepository(csrfTokenRepository());
}
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
public class CsrfCustomFilter extends OncePerRequestFilter{
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
if (csrf != null) {
Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
String token = csrf.getToken();
if (cookie==null || token!=null && !token.equals(cookie.getValue())) {
cookie = new Cookie("XSRF-TOKEN", token);
cookie.setPath("/");
response.addCookie(cookie);
}
}
filterChain.doFilter(request, response);
}
}

Related

How to send token from ASP.net Web API to react js?

I want to implement JWT Authentication in react js using web api.
I had created the JWT Authentication in web api.
It worked totally fine on Postman as I tested it.
When I am using it with react js the API is being hitted.
Now the problem is how do I send the token to react js and how do I fetch the token in react js
This is my Login Controller in web api
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Security.Claims;
using System.Web;
using System.Web.Http;
using System.Web.Http.Cors;
using WEBAPI_JWT_Authentication.Models;
namespace WEBAPI_JWT_Authentication.Controllers
{
[EnableCors(origins: "*", headers: "*", methods: "*")]
public class LoginController : ApiController
{
[HttpPost]
public IHttpActionResult Authenticate([FromBody] LoginRequest login)
{
var loginResponse = new LoginResponse { };
LoginRequest loginrequest = new LoginRequest { };
loginrequest.Username = login.Username.ToLower();
loginrequest.Password = login.Password;
IHttpActionResult response;
HttpResponseMessage responseMsg = new HttpResponseMessage();
bool isUsernamePasswordValid = false;
if(login != null)
isUsernamePasswordValid=loginrequest.Password=="test" ? true:false;
// if credentials are valid
if (isUsernamePasswordValid)
{
string token = createToken(loginrequest.Username);
var responseJSON = token;
//return the token
return Ok(responseJSON);
}
else
{
// if credentials are not valid send unauthorized status code in response
loginResponse.responseMsg.StatusCode = HttpStatusCode.Unauthorized;
response = ResponseMessage(loginResponse.responseMsg);
return response;
}
}
private string createToken(string username)
{
//Set issued at date
DateTime issuedAt = DateTime.UtcNow;
//set the time when it expires
DateTime expires = DateTime.UtcNow.AddDays(7);
//http://stackoverflow.com/questions/18223868/how-to-encrypt-jwt-security-token
var tokenHandler = new JwtSecurityTokenHandler();
//create a identity and add claims to the user which we want to log in
ClaimsIdentity claimsIdentity = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.Name, username)
});
const string sec = "401b09eab3c013d4ca54922bb802bec8fd5318192b0a75f201d8b3727429090fb337591abd3e44453b954555b7a0812e1081c39b740293f765eae731f5a65ed1";
var now = DateTime.UtcNow;
var securityKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(System.Text.Encoding.Default.GetBytes(sec));
var signingCredentials = new Microsoft.IdentityModel.Tokens.SigningCredentials(securityKey,Microsoft.IdentityModel.Tokens.SecurityAlgorithms.HmacSha256Signature);
//create the jwt
var token =
(JwtSecurityToken)
tokenHandler.CreateJwtSecurityToken(issuer:"http://localhost:50191",audience:"http://localhost:50191",
subject: claimsIdentity, notBefore: issuedAt, expires: expires, signingCredentials: signingCredentials);
var tokenString = tokenHandler.WriteToken(token);
return tokenString;
}
}
}
This is where I am fetching the token in react js
function login(username, password) {
return fetch(`${API_URL}/Login`, {username, passowrd})
.then(response => {
debugger;
if (!response.ok) {
return response;
}
return response.json();
})
.then(user => {
debugger;
// login successful if there's a jwt token in the response
if (user && user.token) {
// store user details and jwt token in local storage to keep user logged in between page refreshes
localStorage.setItem('user', JSON.stringify(user));
}
return user;
});
}
Rather than this data if anyone knows how to send token to react js and how to fetch that token in react js, please do tell.
The way I do is to create a Response class
public class Response
{
public string Status { get; set; }
public string Message { get; set; }
public object Token { get; set; }
}
Depending on your need what you want to define in this class, for my cases, Status and Message is used to update progress status to front end.
You store your tokendictionary in class Response, and return it to the function. Lets say:
[HttpPost]
public IHttpActionResult Authenticate([FromBody] LoginRequest login)
{
var loginResponse = new LoginResponse { };
LoginRequest loginrequest = new LoginRequest { };
loginrequest.Username = login.Username.ToLower();
loginrequest.Password = login.Password;
IHttpActionResult response;
HttpResponseMessage responseMsg = new HttpResponseMessage();
bool isUsernamePasswordValid = false;
if(login != null)
isUsernamePasswordValid=loginrequest.Password=="test" ? true:false;
// if credentials are valid
if (isUsernamePasswordValid)
{
string token = createToken(loginrequest.Username);
Response Resp = new Response
{
Status = "Success",
Message = "User Login Successfully Change the Status Message here",
Token = tokenDictonary, //where you return token
};
return
}
else
{
// if credentials are not valid send unauthorized status code in response
loginResponse.responseMsg.StatusCode = HttpStatusCode.Unauthorized;
response = ResponseMessage(loginResponse.responseMsg);
return response;
}
}
and in your front end, you fetch your api.
I show you axios example
axios.post('http://yoururl', {
Token: this.state.Token,
})
.then(result => {
if (result.data.Status === 'Success') {
localStorage.setItem('Nameyourvariablehere', result.data.Token.tokentype);
// generate more if your token has more field
and then you are able to check your localStorage via getItem and setItem, I believe you know what to do for the following steps
Actually the way you create token is different from mine, I kind of follow this example.

spring boot oauth & facebook reference

I'm starting to play around with OAUTH2 in spring boot. I'm currently trying to edit the simple example on this website: https://spring.io/guides/tutorials/spring-boot-oauth2/
But I would like to display more of the person who logs in like email, age and stuff that can be found here:
https://developers.facebook.com/docs/facebook-login/permissions/
Could you guys give me any advice to get any of the information that's described in the url above?
This part is used as login:
<div class="container" ng-show="!home.authenticated">
With Facebook: click here
</div>
Then there is this part in the angular code:
<script type="text/javascript">
angular
.module("app", [])
.config(
function($httpProvider) {
$httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
}).controller("home", function($http, $location) {
var self = this;
$http.get("/user").success(function(data) {
self.user = data.userAuthentication.details.name;
// i added the following line self.details =
self.details = data.userAuthentication.details
self.authenticated = true;
$http.get("")
}).error(function() {
self.user = "N/A";
self.authenticated = false;
});
self.logout = function() {
$http.post('logout', {}).success(function() {
self.authenticated = false;
$location.path("/");
}).error(function(data) {
console.log("Logout failed")
self.authenticated = false;
});
};
});
</script>
Then I put home.details on the screen and it shows this:{"name":"firstname lastname","id":"some-numbers"}
I think /user refers to a method in the socialapplication.java. So I'm wondering where do I edit what information get's retrieved from facebook.
#SpringBootApplication
#RestController
#EnableOAuth2Client
public class SocialApplication extends WebSecurityConfigurerAdapter {
#Autowired
OAuth2ClientContext oauth2ClientContext;
#RequestMapping("/user")
public Principal user(Principal principal) {
return principal;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http.antMatcher("/**").authorizeRequests().antMatchers("/", "/login**", "/webjars/**").permitAll().anyRequest()
.authenticated().and().exceptionHandling()
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/")).and().logout()
.logoutSuccessUrl("/").permitAll().and().csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()).and()
.addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class);
// #formatter:on
}
public static void main(String[] args) {
SpringApplication.run(SocialApplication.class, args);
}
#Bean
public FilterRegistrationBean oauth2ClientFilterRegistration(OAuth2ClientContextFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(filter);
registration.setOrder(-100);
return registration;
}
private Filter ssoFilter() {
OAuth2ClientAuthenticationProcessingFilter facebookFilter = new OAuth2ClientAuthenticationProcessingFilter(
"/login/facebook");
OAuth2RestTemplate facebookTemplate = new OAuth2RestTemplate(facebook(), oauth2ClientContext);
facebookFilter.setRestTemplate(facebookTemplate);
UserInfoTokenServices tokenServices = new UserInfoTokenServices(facebookResource().getUserInfoUri(),
facebook().getClientId());
tokenServices.setRestTemplate(facebookTemplate);
facebookFilter.setTokenServices(
new UserInfoTokenServices(facebookResource().getUserInfoUri(), facebook().getClientId()));
return facebookFilter;
}
#Bean
#ConfigurationProperties("facebook.client")
public AuthorizationCodeResourceDetails facebook() {
return new AuthorizationCodeResourceDetails();
}
#Bean
#ConfigurationProperties("facebook.resource")
public ResourceServerProperties facebookResource() {
return new ResourceServerProperties();
}
Here is the YAML file.
facebook:
client:
clientId: xxxxxxxxxx
clientSecret: xxxxxxxxxxxxxxxxxxxxxx
accessTokenUri: https://graph.facebook.com/oauth/access_token
userAuthorizationUri: https://www.facebook.com/dialog/oauth
tokenName: oauth_token
authenticationScheme: query
clientAuthenticationScheme: form
resource:
userInfoUri: https://graph.facebook.com/me
logging:
level:
org.springframework.security: DEBUG
I'm really at a loss here and hope someone can help me to retrieve more information from facebook.
Thanks for any kind of help!

Angularjs $http get not working

I am trying to access REST web service from angularjs. I am not able to call it successfully.
AngularJs Code
var singleOrderUrl = "/singleOrder/retrieve";
function getSingleOrderDetails(userName,singleOrderUrl,$http,$q) {
var fd = new FormData();
var deffered = $q.defer();
fd.append('USERNAME', 'test123');
//fd.append();
//fd.append();
console.log("inside service"+userName+"singleOrderUrl:::"+singleOrderUrl);
return $http.get(singleOrderUrl, fd, {
withCredentials : false,
transformRequest : angular.identity,
headers : {
'Content-Type' : undefined,
}
}).success(function(response) {
console.log(response);
responseData = response.data.toString();;
deffered.resolve(response);
return responseData;
}).error(function(error) {
alert("error");
deffered.reject(error);
return "failed";
});
};
Rest Service code
#RestController
public class SingleOrderHistoryController {
private static final Logger logger = LoggerFactory.getLogger(SingleOrderHistoryController.class.getName());
#RequestMapping(value = "/singleOrder/retrieve", method=RequestMethod.GET, produces="application/json")
public List<SingleHistoryRecord> getSingleOrderDetails(#RequestParam(value = Constants.USER_NAME, required = true) String userName, HttpServletRequest request,HttpServletResponse response) throws Exception {
logger.debug("inside SingleOrderHistoryController ");
List<SingleHistoryRecord> singleOrderHistoryList = new ArrayList<SingleHistoryRecord>();
SingleHistoryRecord record1 = new SingleHistoryRecord();
SingleHistoryRecord record2 = new SingleHistoryRecord();
record1.setClientIdentifier(userName);
record1.setSubmitDate("01/05/2017");
record1.setStatus("Complete");
record1.setReferenceID("1234555");
record1.setOrderID("test123");
record2.setClientIdentifier(userName);
record2.setSubmitDate("01/05/2017");
record2.setStatus("Complete");
record2.setReferenceID("1234555");
record2.setOrderID("test123");
singleOrderHistoryList.add(record1);
singleOrderHistoryList.add(record2);
return singleOrderHistoryList;
}
Can anyone please advise what I am doing wrong here, It is getting the source code of the page in response instead of getting the list.

angular post request to a asp.net web API server

IN angular js i am trying to send a post request her is the controller.
.controller('ActivationController',['$http','$location','$routeParams','AuthService',
function($http, $location, $routeParams, AuthService){
var location = $location.path();
var activation_code = $routeParams.code;
var activationLink = "http://localhost:18678/api/User/ActivateUser";
console.log(activation_code);
if(activation_code){
$http({method:"post", url:activationLink, data:activation_code}).success(function(response){
console.log(response);
}).error(function(error){
console.log(error);
});
}
}]);
and in asp.net web API her is the method.
[HttpPost]
public HttpResponseMessage ActivateUser([FromBody]string activation_code)
{
if (!string.IsNullOrWhiteSpace(activation_code))
{
string decode_token = HttpUtility.UrlDecode(activation_code); ;
string token_string = Crypto.Decrypt(activation_code, passPhrase);
if (token_string != null)
{
User activateAcc = db.Users.Where(user => user.ConfirmToken == token_string).SingleOrDefault();
if (activateAcc != null)
{
activateAcc.IsActive = true;
try
{
db.SaveChanges();
var credential = new UserCredential();
credential.EmailAddress = activateAcc.UserMail;
credential.Password = activateAcc.UserPassword;
return Request.CreateResponse(HttpStatusCode.OK, credential);
}
catch
{
return Request.CreateResponse(HttpStatusCode.Ambiguous, "cannot confirm account");
}
}
else
{
return Request.CreateResponse(HttpStatusCode.NotAcceptable, "invalid account");
}
}
else
{
return Request.CreateResponse(HttpStatusCode.NoContent, "invalid token data");
}
}
else
{
return Request.CreateResponse(HttpStatusCode.NoContent, "missing activation code");
}
}
The problem is when the controller fire the request is made but without sending any data to the server. The [FromBody]string activation_codeis null
Wrap your parameter in quotes:
$http({method:"post", url:activationLink, data: '"' + activation_code + '"'});
Explanation
For Web API to bind to a simple string primitive, the body must be specified as:
"some string here"
For example:
POST http://localhost:5076/api/values HTTP/1.1
User-Agent: Fiddler
Host: localhost:5076
Content-Type: application/json
Content-Length: 7
"Alice"
The quotes are important. For more information, check this article.

Google App Engine. Channel API. The provided token is invalid.

I've been trying to make an application for calling browser to browser using WebRTC. I wrote a simple servlet for creating channel:
PrefClubChannelServletServlet extends HttpServlet {
public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
String channelKey = req.getParameter("userid");
resp.addHeader("Access-Control-Allow-Origin", "*");
ChannelService channelService = ChannelServiceFactory.getChannelService();
String token = channelService.createChannel(channelKey);
resp.setContentType("text/plain");
resp.getWriter().println(token);
}
}
I've deployed it in Google App Engine.
In my web application I've got a page with Java script, similar to https://apprtc.appspot.com. In my code Caller calls prepare(1), and Callee – prepare(0).
function prepare(ini) {
initiator = ini;
card = document.getElementById("card");
localVideo = document.getElementById("localVideo");
miniVideo = document.getElementById("miniVideo");
remoteVideo = document.getElementById("remoteVideo");
resetStatus();
console.log("Try to get token");
getToken();
}
function getToken() {
var token;
if (initiator) {
var xhr = new XMLHttpRequest();
var url = 'http://pref-club.appspot.com/prefclubchannelservlet?userid=GGGG';
xhr.open('POST', url, true);
// Specify that the body of the request contains form data
xhr.setRequestHeader("Content-Type",
"application/x-www-form-urlencoded");
xhr.send(url);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
token = xhr.responseText;
console.log("token = " + token);
document.getElementById("token").value = token;
openChannel(token);
doGetUserMedia();
}
};
} else {
token = document.getElementById("token").value;
console.log("token = " + token);
openChannel(token);
doGetUserMedia();
}
};
So, both Caller and Callee use the same token to open channel, and indeed they open channel, got media and made RTCPeerConnection
function openChannel(channelToken) {
console.log("Opening channel.");
var channel = new goog.appengine.Channel(channelToken);
var handler = {
'onopen': onChannelOpened,
'onmessage': onChannelMessage,
'onerror': onChannelError,
'onclose': onChannelClosed
};
socket = channel.open(handler);
}
The main problem is that event handler onChannelMessage doesn't work. I don't see any S->C: in console log. Callee doesn't see offer by Caller.
Then, I refreshed my servlet, redeployed it, and discovered I can't open channel at all. While Opening channel I'm getting Uncaught Error:
The provided token is invalid.

Resources