Problems to get a refresh token using vue, nuxt and keycloak - vue.js

I'm doing a project with vue, nuxt and keycloak as server for token, axios as http client and #nuxtjs/auth-next module for keycloak access.
I'm using a public client so I don't have a secret key which is the most recommended.
The part of getting the token and talking to the backend is working.
But as it is a public client it has no refresh token.
Searching the internet, a recommendation would be to post from time to time to the keycloak /token endpoint, passing the current token, to fetch a new token.
To perform this post, it doesn't work to pass json, having to pass application/x-www-form-urlencoded.
But it generates an error saying that the parameter was not passed.
On the internet they recommended passing it as url string, but then it generates an error on the keycloak server, as a parameter that is too long, because of the current token that is passed.
Below is the code used to try to fetch a new token.
This code is being called on a test-only button.
If anyone can help, I appreciate it.
const token = this.$auth.strategy.token.get()
const header = {
"Content-Type": "application/x-www-form-urlencoded"
}
const body = {
grant_type: "authorization_code",
client_id: "projeto-ui",
code: token
}
this.$axios ( {
url: process.env.tokenUrl,
method: 'post',
data: body,
headers: header
} )
.then( (res) => {
console.log(res);
})
.catch((error) => {
console.log(error);
} );

Good afternoon people.
Below is the solution to the problem:
On the keycloak server:
it was necessary to put false the part of the implicit flow.
it was necessary to add web-origins: http://localhost:3000, to allow CORS origins.
In nuxt.config.js it was necessary to modify the configuration, as below:
auth: {
strategies: {
keycloak: {
scheme: 'oauth2',
...
responseType: 'code',
grantType: 'authorization_code',
codeChallengeMethod: 'S256'
}
}
}

Related

Invalid csrf token with NestJS

I would like to implement Csrf protection with NestJS and Quasar.
But I think I misunderstand something...
btw I'm not doing SSR, so I don't send the form from the back to the view.
Here is the NestJs back-end code:
async function bootstrap() {
const PORT = process.env.PORT;
const app = await NestFactory.create(AppModule, {
cors: true,
bodyParser: false,
});
console.log(`your App is listening on port ${PORT}`);
// Added Cookie-parser to user csurf packages
// Prevent CSRF attack
app.use(cookieParser());
app.use(csurf({ cookie: true }));
await app.listen(PORT);
}
bootstrap();
So I'm just using CookieParser and csurf package.
On my login page I call a "csrf endpoint" just to send a cookie to the view, to send it back with the post call (login).
I still get the "invalid csrf token" AND a CORS error and don't know why....(see screen below), any suggestions to make it works ?
When I try to login, error in the browser:
And error in the back-end:
Same error if I try a request with insomnia.
I thought that the CSRF token is attached to the "web browser" to go back to the back-end with nest request, so why I'm still getting this error ?
Insomnia send the cookie automatically with the right request so the token should go back to the back-end.
Any idea ?
Regards
EDIT:
After many times reading docs, It seems that CSRF protection is for SSR only ? No need to add csrf security with SPA ? Could anyone can confirm ?
EDIT: Here's another work:
The purpose here is to send a request before login to get a csrf token that I can put into a cookie to resend when I login with a POST method.
Here is my endpoint:
import { Controller, Get, Req, Res, HttpCode, Query } from "#nestjs/common";
#Controller("csrf")
export class SecurityController {
#Get("")
#HttpCode(200)
async getNewToken(#Req() req, #Res() res) {
const csrfToken = req.csrfToken();
res.send({ csrfToken });
}
}
Here is what I've done into my main.ts file (I'll explain below):
async function bootstrap() {
const PORT = process.env.PORT;
const app = await NestFactory.create(AppModule, {
cors: {
origin: "*",
methods: ["GET,HEAD,OPTIONS,POST,PUT"],
allowedHeaders: [
"Content-Type",
"X-CSRF-TOKEN",
"access-control-allow-methods",
"Access-Control-Allow-Origin",
"access-control-allow-credentials",
"access-control-allow-headers",
],
credentials: true,
},
bodyParser: false,
});
app.use(cookieParser());
app.use(csurf({ cookie: true }));
console.log(`your App is listening on port ${PORT}`);
await app.listen(PORT);
}
bootstrap();
And here my axiosInstance Interceptors of the request in my VueJS frontend:
axiosInstance.interceptors.request.use(
(req) => {
const token = Cookies.get('my_cookie')
if (token) {
req.headers.common['Authorization'] = 'Bearer ' + token.access_token
}
req.headers['Access-Control-Allow-Origin'] = '*'
req.headers['Access-Control-Allow-Credentials'] = 'true'
req.headers['Access-Control-Allow-Methods'] = 'GET,HEAD,OPTIONS,POST,PUT'
req.headers['Access-Control-Allow-Headers'] =
'access-control-allow-credentials,access-control-allow-headers,access-control-allow-methods,access-control-allow-origin,content-type,x-csrf-token'
const csrfToken = Cookies.get('X-CSRF-TOKEN')
if (csrfToken) {
req.headers['X-CSRF-TOKEN'] = csrfToken
console.log(req)
}
return req
},
(err) => {
console.log(err)
},
Here the same for repsonse:
axiosInstance.interceptors.response.use(
(response) => {
if (response?.data?.csrfToken) {
const {
data: { csrfToken },
} = response
Cookies.set('X-CSRF-TOKEN', csrfToken)
}
return response
},
And inside my login I make a call on the mounted function of my login component:
async mounted() {
const result = await securityService.getCsrf()
},
So now to explain:
As I said I'm not building a SSR project, that's why I want to send the token into a classic axios reponse and store it in a Cookie (this part is for test I heard that storing a csrf token into a classic cookie is not the right way.)
And for each next request I get the csrf token and "attach" it to the request into the headers, making my headers "custom".
Here is a problem I don't know how to make custom headers works with nestJS and CORS, that's why I try many thing with CORS options in NestJS and writte some custome header before the request go to the back-end but without success, I've got the same error message:
I'm a bit confuse about this problem and CORS/CSRF is a big deal for spa, my questions still the same, with CORS and SameSite cookie attributes, and my api is in a subdomain of my front-end, is it really necessary to make a anti-csrf pattern ?
Btw how can I make my custom headers working and why CORS say to me there is no "Access-Control-Allow-Origin" header but there is:
try to generate csrf token and pass to front on each petition
// main.ts - from NestJs - Backend
// after app.use(csurf({ cookie: true }))
app.use((req: any, res: any, next: any) => {
const token = req.csrfToken()
res.cookie("XSRF-TOKEN", token)
res.locals.csrfToken = token
next()
})
from: https://github.com/nestjs/nest/issues/6552#issuecomment-1175270849

How to properly use passport-github for REST API authentication?

I am building a vue.js client which needs to be authenticated through github oauth using an express server. It's easy to do this using server side rendering but REST API has been troublesome for me.
I have set the homepage url as "http://localhost:3000" where the server runs and I want the authorization callback url to be "http://localhost:8080" (which hosts the client). I am redirecting to "http://localhost:3000/auth/github/redirect" instead, and in its callback redirecting to "http://localhost:8080". The problem I am facing is that I am unable to send user data to the vuejs client through res.redirect. I am not sure if I am doing it the right way.
router.get("/github", passport.authenticate("github"));
router.get(
"/github/redirect",
passport.authenticate("github", { failureRedirect: "/login" }),
(req, res) => {
// res.send(req.user);
res.redirect("http://localhost:8080/"); // req.user should be sent with this
}
);
I have implemented the following approach as a work around :-
A route that returns the user details in a get request :
router.get("/check", (req, res) => {
if (req.user === undefined) {
res.json({});
} else {
res.json({
user: req.user
});
}
});
The client app hits this api right after redirection along with some necessary headers :
checkIfLoggedIn() {
const url = `${API_ROOT}auth/check/`;
return axios(url, {
headers: { "Content-Type": "application/json" },
withCredentials: true
});
}
To enable credentials, we have to pass the following options while configuring cors :
var corsOption = {
origin: true,
credentials: true
};
app.use(cors(corsOption));

Accessing the response from one GET request within another

I'm working with Vue to interact with an external API on a Drupal website, but in order to do so dynamically per Drupal user, I need to get a token from Drupal first. To do so, I'm trying to do two GET requests. The first gets me a bearer token out of Drupal, and the second uses it to authenticate the third-party API request.
Below is what I'm trying – I'm able to get the token successfully in the first request's response, but not when I try to use it in the header of my second request. If I try hardcoding the token that I see in the console log, it does work, so I know none of that is the issue. It's just that this.jwt['data']['token'] in the second request's headers seems to not pull back the token.
What do I need to adjust in order to access the token from the first response as part of the headers of my second request?
created() {
axios
.get('/jwt/token')
.then(response => {
this.jwt = response
console.log(this.jwt['data']['token']) // this does show what I want to access later
})
},
mounted() {
axios
.get('/comment/doc/' + this.id, {
headers: { Authorization: "Bearer " + this.jwt['data']['token'] } // ...but this doesn't work
})
.then(response => {
this.comments = response
})
},
It's likely the response to the token request has not finished by the time the component mounts, at which point this.jwt is not yet assigned.
I would move the token request into the mounted hook, fetching comments only when the token request succeeds:
export default {
mounted() {
axios
.get('/jwt/token')
.then(tokenResp => {
this.jwt = tokenResp
axios
.get('/comment/doc/' + this.id, {
headers: { Authorization: 'Bearer ' + this.jwt['data']['token'] }
})
.then(commentsResp => {
this.comments = commentsResp
})
})
}
}

AWS-amplify Including the cognito Authorization header in the request

I have create an AWS mobile hub project including the Cognito and Cloud logic. In my API gateway, I set the Cognito user pool for the Authorizers. I use React native as my client side app. How can I add the Authorization header to my API request.
const request = {
body: {
attr: value
}
};
API.post(apiName, path, request)
.then(response => {
// Add your code here
console.log(response);
})
.catch(error => {
console.log(error);
});
};
By default, the API module of aws-amplify will attempt to sig4 sign requests. This is great if your Authorizer type is AWS_IAM.
This is obviously not what you want when using a Cognito User Pool Authorizer. In this case, you need to pass the id_token in the Authorization header, instead of a sig4 signature.
Today, you can indeed pass an Authorization header to amplify, and it will no longer overwrite it with the sig4 signature.
In your case, you just need to add the headers object to your request object. For example:
async function callApi() {
// You may have saved off the JWT somewhere when the user logged in.
// If not, get the token from aws-amplify:
const user = await Auth.currentAuthenticatedUser();
const token = user.signInUserSession.idToken.jwtToken;
const request = {
body: {
attr: "value"
},
headers: {
Authorization: token
}
};
var response = await API.post(apiName, path, request)
.catch(error => {
console.log(error);
});
document.getElementById('output-container').innerHTML = JSON.stringify(response);
}
Tested using aws-amplify 0.4.1.

Laravel Passport 401 Unauthorized Error using Apache and Vue

i am trying to connect a generate a laravel user API using vue and laravel passport but i keep getting an authorization error in my headers . This ismy code
<script>
import Hello from './components/Hello'
export default {
name: 'app',
components: {
Hello
},
created () {
const postData = {
grant_type: 'password',
client_id: 2,
client_secret: 'sXdg5nOO4UU2muiHaQnTq4hDQjyj17Kd9AeKuNEx',
username: 'robertrutenge#gmail.com',
password: 'password',
scope: ''
}
this.$http.post('http://localhost:8000/oauth/token', postData)
.then(response => {
console.log(response)
const header = {
'Accept': 'application/json',
'Authorization': ~'Bearer ' + response.body.access_token
}
this.$http.get('http://localhost:8000/api/user', {headers: header})
.then(response => {
console.log(response)
})
})
}
}
</script>
I have done a research and most answers suggest modifying apache config file or .htaccess file but that also does not seem to work on my end . Any help will be appreciated :-)
i think if your vue app and laravel app is combine, then you can use your api without any authorization header you just need to send X-CSRF-TOKEN and send that token with each request no need to send authorization check here
https://laravel.com/docs/5.3/passport#consuming-your-api-with-javascript
this is not problem on in vue.js or larvel. i moved my l Laravel API from Apache to nginx then working fine. i updated my middleware handler like this. then working fine on Apache server
$origin = $request->server()['HTTP_ORIGIN'];
if(in_array($origin, $url)){
header('Access-Control-Allow-Origin: '. $origin);
header('Access-Control-Allow-Headers: Origin, Content-Type, Accept, Authorization, X-Csrf-Token');
}
resolve (2)
go to
AuthServiceProvider.php
keypoint:set expiration of token
Passport::tokensExpireIn(Carbon::now()->addDays(1));
You must to set token expiration
cant be infinite