Google App Engine. Channel API. The provided token is invalid. - google-app-engine

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.

Related

Failed to submit credentials because of connector error

I'm trying to build my own connector for the first time. I'm getting the following error:
Sorry, we failed to submit your credentials because there was an error in the connector in processing the credentials.
Here is my code so far:
function getAuthType() {
var cc = DataStudioApp.createCommunityConnector();
return cc.newAuthTypeResponse()
.setAuthType(cc.AuthType.USER_TOKEN)
.setHelpUrl('https://api-doc-help-url/authentication')
.build();
}
function validateCredentials(userName, token) {
var rawResponse = UrlFetchApp.fetch('https://api/v2/stuff/' + userName , {
method: 'GET',
headers: {
'Authorization': userName + ':' + token
},
muteHttpExceptions: true
});
return rawResponse.getResponseCode() === 200;
}
function isAuthValid() {
var userProperties = PropertiesService.getUserProperties();
var userName = userProperties.getProperty('dscc.username');
var token = userProperties.getProperty('dscc.token');
return validateCredentials(userName, token);
}
function setCredentials(request) {
var creds = request.userToken;
var username = creds.username;
var token = creds.token;
var validCreds = checkForValidCreds(username, token);
if (!validCreds) {
return {
errorCode: 'INVALID_CREDENTIALS'
};
}
var userProperties = PropertiesService.getUserProperties();
userProperties.setProperty('dscc.username', username);
userProperties.setProperty('dscc.token', token);
return {
errorCode: 'NONE'
};
}
I'm not sure how all this work exactly. I know that I need to pass the Authorization in the header with the following value userName:token
When deploying my manifest and clicking the Google Data Studio link I'm redirected to the select connector data studio interface. The error appear when I'm trying to submit Usernam and Token.
checkForValidCreds is not defined inside the function setCredentials. which is causing the error

Spring get request file not being downloaded

I want to download a file when clicking on a button in my AngularJS app which runs on Tomcat with a Java Spring backend but nothing is happening. The method in the backend is called and everything seems to have worked....but my browser doesn't download anything.
What am I missing?
Here's the AngularJS code, which logs Export-Response:[object Object]:
exportProjects() {
let filteredProjectIds = [];
for (let i in this.filteredProjects) {
for (let x = 0, l = this.filteredProjects[i].length; x < l; x++) {
if (!this.isOldProjectsBundle(this.filteredProjects[i][x])) {
filteredProjectIds.push(this.filteredProjects[i][x].id);
}
}
}
this.$http.get('/profiles/projectWordExport?filteredProjects=' + filteredProjectIds.join(",")).then(response => {
console.log("Export-Response:" + response);
return response;
});
}
This is the Java code being called (it's really being called, already debugged it, no errors occuring):
#RequestMapping(value = "/projectWordExport", method = RequestMethod.GET)
public void getProjectsWord(HttpServletRequest request, HttpServletResponse response, #RequestParam String filteredProjects) throws Exception {
//Load project objects from input string or load all projects if input empty
List<Project> projects = new java.util.ArrayList<>();
if (filteredProjects.isEmpty()) {
projects = projectRepository.findAll();
} else {
String[] pIds = filteredProjects.split(",");
for (String pId : pIds) {
projects.add(projectRepository.findById(Long.parseLong(pId)));
}
}
response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
response.setHeader("Content-disposition", "attachment;filename=Projektexport.docx");
try {
SaveToZipFile saver = new SaveToZipFile(printer.printProjects(this.prepareProjectExport(projects)));
saver.save(response.getOutputStream());
response.flushBuffer();
} catch (NullPointerException e) {
response.setStatus(500);
response.sendError(500, "Fehler beim exportieren des Tests aufgetreten");
}
}
Put this in #RequestMapping annotation
produces = MediaType.APPLICATION_OCTET_STREAM_VALUE

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.

gmail API for sending users messages in nodejs javascript failes

My nodejs program fails to send messages using the Gmail api.
The solution from Gmail API for sending mails in Node.js does not work for me.
I encode an email with
var {google} = require('googleapis');
// to and from = "some name <blaw.blaw.com"
function makeBody(to, from, subject, message) {
var str = ["Content-Type: text/plain; charset=\"UTF-8\"\r\n",
"MIME-Version: 1.0\r\n",
"Content-Transfer-Encoding: 7bit\r\n",
"to: ", to, "\r\n",
"from: ", from, "\r\n",
"subject: ", subject, "\r\n\r\n",
message
].join('');
encodedMail = new Buffer(str).toString("base64").replace(/\+/g, '-').replace(/\//g, '_');
return encodedMail;
}
Then go to the Google API explorer
https://developers.google.com/apis-explorer/#p/
enter gmail.users.messages.send and the string generated from the above make_body.
An email will be successfully sent. So I know the above encoding is
ok.
When my program tried to send using the following, it fails with error
Error: 'raw' RFC822 payload message string or uploading message via
/upload/* URL required
function sendMessage(auth) {
var gmail = google.gmail('v1');
var raw = makeBody('john g <asdfasdf#hotmail.com>', 'john g<asfasdgf#gmail.com>', 'test subject', 'test message #2');
gmail.users.messages.send({
auth: auth,
userId: 'me',
resource: {
raw: raw
}
}, function(err, response) {
console.log(err || response)
});
}
The auth token is good since I can call gmail.users.labels.list and I use the same authorization when using the API explorer.
Q1: Does anyone know why the above does not work?
Q2: Gmail API for sending mails in Node.js does not explain why the raw email message is wrapped inside a resource field. I tried simply raw and it did not help.
This fails.
gmail.users.messages.send({
auth: auth,
userId: 'me',
resource: {
raw: raw
}
}, function(err, response) {
console.log(err || response)
});
and so does
gmail.users.messages.send({
auth: auth,
userId: 'me',
raw: raw
}, function(err, response) {
console.log(err || response)
});
and so does this GMAIL API for sending Email with attachment
gmail.users.messages.send({
auth: auth,
userId: 'me',
data: raw
}, function(err, response) {
console.log(err || response)
});
Does anyone know where its documented how to pass the "requested body" the api explorer is asking for?
Q3: Why does the google api need substitutions in the base64 encoding?
I tried encoding using
const Base64 = require("js-base64").Base64
var encodedMail = Base64.encode(str);
When I feed this into the API explorer, I get the error
"message": "Invalid value for ByteString:
Ohai! For others that stumble here, a few things. First - we have a complete end to end sample of sending mail now here:
https://github.com/google/google-api-nodejs-client/blob/master/samples/gmail/send.js
Second, the answer above is mostly right :) Instead of installing the latest version of google-auth-library... just remove it from your package.json all together. The getting started guide was very, very wrong (it has since been fixed). googelapis brings in it's own compatible version of google-auth-library, so you really don't want to mess with that by installing your own version :)
The quickstart specifies:
npm install google-auth-library#0.* --save
When I changed this to
npm install google-auth-library -- save
it pulled in version 1.3.1 vs 0.12.0. Everything started working once I changed the code to account for the breaking changes. The latest version of googleapis also has breaking changes. Here is my tweaks to the quickstart:
package.json
....
"dependencies": {
"google-auth-library": "^1.3.1",
"googleapis": "^26.0.1"
}
quickstart.js
var fs = require('fs');
var readline = require('readline');
var {google} = require('googleapis');
const {GoogleAuth, JWT, OAuth2Client} = require('google-auth-library');
var SCOPES = [
'https://mail.google.com/',
'https://www.googleapis.com/auth/gmail.modify',
'https://www.googleapis.com/auth/gmail.compose',
'https://www.googleapis.com/auth/gmail.send'
];
var TOKEN_DIR = (process.env.HOME || process.env.HOMEPATH ||
process.env.USERPROFILE) + '/.credentials/';
var TOKEN_PATH = TOKEN_DIR + 'gmail-nodejs-quickstart.json';
function authorize(credentials, callback) {
var clientSecret = credentials.installed.client_secret;
var clientId = credentials.installed.client_id;
var redirectUrl = credentials.installed.redirect_uris[0];
var auth = new GoogleAuth();
var oauth2Client = new OAuth2Client(clientId, clientSecret, redirectUrl);
// Check if we have previously stored a token.
fs.readFile(TOKEN_PATH, function (err, token) {
if (err) {
getNewToken(oauth2Client, callback);
} else {
oauth2Client.credentials = JSON.parse(token);
callback(oauth2Client);
}
});
}
function getNewToken(oauth2Client, callback) {
var authUrl = oauth2Client.generateAuthUrl({
access_type: 'offline',
scope: SCOPES
});
console.log('Authorize this app by visiting this url: ', authUrl);
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.question('Enter the code from that page here: ', function (code) {
rl.close();
oauth2Client.getToken(code, function (err, token) {
if (err) {
console.log('Error while trying to retrieve access token', err);
return;
}
oauth2Client.credentials = token;
storeToken(token);
callback(oauth2Client);
});
});
}
function makeBody(to, from, subject, message) {
var str = ["Content-Type: text/plain; charset=\"UTF-8\"\n",
"MIME-Version: 1.0\n",
"Content-Transfer-Encoding: 7bit\n",
"to: ", to, "\n",
"from: ", from, "\n",
"subject: ", subject, "\n\n",
message
].join('');
var encodedMail = new Buffer(str).toString("base64").replace(/\+/g, '-').replace(/\//g, '_');
return encodedMail;
}
function sendMessage(auth) {
var gmail = google.gmail('v1');
var raw = makeBody('xxxxxxxx#hotmail.com', 'xxxxxxx#gmail.com', 'test subject', 'test message');
gmail.users.messages.send({
auth: auth,
userId: 'me',
resource: {
raw: raw
}
}, function(err, response) {
console.log(err || response)
});
}
const secretlocation = 'client_secret.json'
fs.readFile(secretlocation, function processClientSecrets(err, content) {
if (err) {
console.log('Error loading client secret file: ' + err);
return;
}
// Authorize a client with the loaded credentials, then call the
// Gmail API.
authorize(JSON.parse(content), sendMessage);
});
Now when I run, I get the response
Object {status: 200, statusText: "OK", headers: Object, config: Object, request: ClientRequest, …}

Spring Security cookie arrives Angular only after first forbidden request

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);
}
}

Resources