I'm new to Loopback and Reactjs. I wanted to build an application with User login, logout functionality. But, I'm confused how to set the Access Token in Loopback with react front end and further access the other methods. I'm using the User Model provided with Loopback.
So far, I've written this little code, for access login, but I'm confused how to further set the access Token to authenticate.
import React, {Component} from 'react';
import axios from 'axios';
class Login extends Component{
constructor(props) {
super(props);
this.state = {
"username": ""
}
}
login(newUser) {
axios.request({
method:'post',
url:'http://localhost:3000/api/Users/login',
data: newUser
}).then(response => {
this.props.history.push('/');
}).catch(err => console.log(err));
}
onSubmit(e){
const newUser = {
username: this.refs.username.value,
password: this.refs.password.value
}
this.login(newUser);
e.preventDefault();
}
}
This code snippet as expected does not set the Access Token, so I was wondering if I require some additional middleware or something to do it.
try this code.
login(newUser) {
axios.request({
method:'post',
url:'http://localhost:3000/api/Users/login',
data: newUser
}).then(response => {
localStorage.ptspotter_accessToken = response.data.id;
localStorage.ptspotter_userId = response.data.userId
auth0.login();
this.props.history.push('/');
}).catch(err => console.log(err));
window.login();
}
you can store login userId and token in localStorage and access anywhere.
Related
I have a react frontend with auth0 to log users, once a user is logged in I get the token with getAccessTokenSilently() and send it to the backend like this:
const { user, isAuthenticated, getAccessTokenSilently } = useAuth0()
useEffect(() => {
if (user) getTickets()
}, [user])
async function getTickets() {
const token = await getAccessTokenSilently()
const response = await fetch('http://localhost:4000/api/gettickets', {
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ user, bar }),
})
const data = await response.json()
}
Once I have the token in my backend I try to verify it jsonwebtoken like this:
import express from 'express'
import jwt from 'jsonwebtoken'
import dotenv from 'dotenv'
dotenv.config()
const routerGetTickets = express.Router()
routerGetTickets.post('/', async (req, res) => {
const PUBKEY = process.env.PUBKEY
const token = req.headers.authorization?.split(' ')[1]
if (token && PUBKEY) {
jwt.verify(token, PUBKEY, { algorithms: ['RS256'] }, (err, data) => {
console.log('token :>> ', token)
if (err) {
res.sendStatus(403)
console.log('err :>> ', err)
return
} else {
console.log('everything ok')
}
})
}
})
export default routerGetTickets
If I'm not wrong, with algorithm RS256 I have to provide the public key witch I got using openssl with the signin certification I downloaded from my aplication in Auth0 dashboard.
This is the error I get: err :>> JsonWebTokenError: secretOrPublicKey must be an asymmetric key when using RS256
And this is my index.ts:
import React from 'react'
import ReactDOM from 'react-dom/client'
import './sass/index.scss'
import App from './App'
import { BrowserRouter } from 'react-router-dom'
import { Auth0Provider } from '#auth0/auth0-react'
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement)
root.render(
<React.StrictMode>
<BrowserRouter basename='/tombola'>
<Auth0Provider
domain='*******.uk.auth0.com'
clientId='*****************************'
authorizationParams={{
redirect_uri: `${window.location.origin}/tombola/callback`,
audience: 'https://******.uk.auth0.com/api/v2/',
scope: 'read:current_user update:current_user_metadata'
}}
>
<App />
</Auth0Provider>
</BrowserRouter>
</React.StrictMode>
)
I had this issue in the verify jwt method also using Auth0, the problem is with the public key.
You didn't show your .env file where the PUBKEY variable are but I imagine you got it from the Application -> settings -> advanced settings -> certificates, as the Auth0 docs says, but thats the certificate not the pubkey.
To get the public key you need to use the "jwks-rsa" library, it retrieves the publickey using the certificate, getting it from your Auth0 application domain, and for this it needs two things:
1- The domain to construct the url that Auth0 serve the keys.
2- the "kid" header of your jwt token, that identified the correct key.
The code look like this:
async function HandleRequest(req, res) {
const cookie = req.headers.cookie;
const token = cookie.split(";")
.find(c => c.trim().startsWith("access_token="))
.split("=")[1]
const kid = decode(token, { complete: true }).header.kid;
const publicKey = (await JwksRsa({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 5,
jwksUri: `https://${process.env.DOMAIN}/.well-known/jwks.json`
}).getSigningKey(kid)).getPublicKey()
if (cookie && cookie.includes("access_token")) {
verify(
token,
publicKey,
{
algorithms: ["RS256"],
},
(err, decoded) => {
if (err) {
res.write(JSON.stringify(err));
} else {
res.write(JSON.stringify(decoded));
}
}
);
} else {
res.write("No access token found")
}
res.end();
return
}
Note that in the example, I'm in a api route, dealing with a request and getting the jwt token in the headers, my jwt is in the headers as "access_token", yours could be different, then decoding it, extracting the kid identifier, passing it to the jwks-rsa alongside the domain constructed url, and then the jwks-rsa library is dealing with all request to get the certificate from your Auth0 application, and all the cryptography work of retrieving the public key from the certificate.
You can retrieve from the certificate using "crypto" node module, but I tried and it was a mess because it works bettem with .pem files than with strings, but it's a useful thing to know.
Hope this helps you, I know that Auth0 can mess with our heads while learning.
Thanks! I had the same issue and resolved it as you suggested.
I used this documentation(https://github.com/panva/node-oidc-provider/blob/main/docs/README.md#accounts)for implementing OpenID to Nest JS. In this documentation he mentioned client_id and client secret and redirect URLS. How to get this Information's and Integrating
One option is to create an oidc strategy for passport.
It's a lengthy process, and rather than copying/pasting an entire tutorial, I'll add a link and hope it doesn't break.
https://sdoxsee.github.io/blog/2020/02/05/cats-nest-nestjs-mongo-oidc.html
Here's the strategy implementation, but there are several other components that need to be configured.
// auth/oidc.strategy.ts
import { UnauthorizedException } from '#nestjs/common';
import { PassportStrategy } from '#nestjs/passport';
import { Strategy, Client, UserinfoResponse, TokenSet, Issuer } from 'openid-client';
import { AuthService } from './auth.service';
export const buildOpenIdClient = async () => {
const TrustIssuer = await Issuer.discover(`${process.env.OAUTH2_CLIENT_PROVIDER_OIDC_ISSUER}/.well-known/openid-configuration`);
const client = new TrustIssuer.Client({
client_id: process.env.OAUTH2_CLIENT_REGISTRATION_LOGIN_CLIENT_ID,
client_secret: process.env.OAUTH2_CLIENT_REGISTRATION_LOGIN_CLIENT_SECRET,
});
return client;
};
export class OidcStrategy extends PassportStrategy(Strategy, 'oidc') {
client: Client;
constructor(private readonly authService: AuthService, client: Client) {
super({
client: client,
params: {
redirect_uri: process.env.OAUTH2_CLIENT_REGISTRATION_LOGIN_REDIRECT_URI,
scope: process.env.OAUTH2_CLIENT_REGISTRATION_LOGIN_SCOPE,
},
passReqToCallback: false,
usePKCE: false,
});
this.client = client;
}
async validate(tokenset: TokenSet): Promise<any> {
const userinfo: UserinfoResponse = await this.client.userinfo(tokenset);
try {
const id_token = tokenset.id_token
const access_token = tokenset.access_token
const refresh_token = tokenset.refresh_token
const user = {
id_token,
access_token,
refresh_token,
userinfo,
}
return user;
} catch (err) {
throw new UnauthorizedException();
}
}
}
You get the client-id and secret from the openid connect provider. You add/register the client there.
Redirect URL is the URL to the openid connect client, to what URL the authorization code should be sent to after a successful authentication. This URL is hardcoded in the provider.
I have created login form with angular 8 and node js. I have set the session using node js in back end. i couldnt check session set or not in angular for avoid access dashboard without logged in. Kindly suggest the way to use login system using angular 8 and node js. Thanks.....
A very popular method is to use JWT (JSON Web Tokens) npm package to authenticate.
The process would be:
Send credentials to the server
Server generates and sends back JWT or a Bearer Token
FrontEnd would store it in browser cookies or localStorage
localStorage.setItem('TOKEN', tokenReceivedFromServer);
In subsequent Api Calls the token would be sent to the server in a Header (Authorization).
Authorization: `JWT ${localStorage.getItem('TOKEN')}`
FYI: JWT keyword is removed from string on the server before parsing token
The frontend can check if the token is set in storage to show login page / dashboard
First we need to check the login credentials valid or not in application.
In angular application component typescript file, we have send the data service in argument, the service send the values to backend using httpclient. If credentials valid we set the value in localstorage.
submitLogin(data:any)
{
this.LoginService.loginData(data).subscribe(data =>{
if(data.body.status_code == 404)
{
Swal.fire({
icon: 'warning',
title: 'Invalid E-Mail/Password!',
}).then(function(){
});
}else if(data.body.status_code ==200)
{
localStorage.setItem("user_id",data.body.token);
this.router.navigate(['/Dashboard']);
}else
{
Swal.fire({
icon: 'error',
title: 'Process Failed!',
}).then(function(){
});
}
});
}
In service.ts file make sure about those packages import
import { HttpClient } from '#angular/common/http';
import { Observable, throwError } from 'rxjs';
import {Login} from './login';
in loginData function
url = "http://localhost:3000/loginCheck";
loginData(Login:Login):Observable<any>
{
return this.http.post(this.url,Login,{observe: 'response'});
}
in backend i have used node.js
in file app.js
first install jsonwebtoken package and include in the file.
npm install jsonwebtoken
then set the jsonwebtoken when where condition satisfies
let payload = {subject:employee_id}
let token = jwt.sign(payload,'secretKey')
var response = {
'token': token,
"status_code":200,
}
res.send(response);
res.end();
Whenever we use the login in angular we must use the authguard routing its helps to access dashboard without logged in.
ng generate guard auth
in auth.guard.ts file we must include the package and service
import { CanActivate, Router } from '#angular/router';
import {LoginService} from './login.service';
export class AuthGuard implements CanActivate {
constructor(private LoginService:LoginService,private router:Router) {}
canActivate(): boolean
{
if(this.LoginService.loggedIn())
{
return true
}else
{
this.router.navigate(['/login']);
return false;
}
}
}
In this file we just checking the localstorage value set or not in boolean datatype.
in service file
add the following code for get and return in boolean type
loggedIn()
{
return !!localStorage.getItem('user_id')
}
getToken()
{
return localStorage.getItem('user_id')
}
if its returns true we can access the dasboard, else its redirected to login page.
We must use this canActive function in routing otherwise it will not working
In app-routing.module.ts file
import { AuthGuard } from './auth.guard';
const routes: Routes = [
{path:'Dashboard',component:DashboardComponent},
{path:'receipt',component:ReciptComponentComponent,canActivate:[AuthGuard]},
];
It will helpus to access dashboard without loggedin but we need to check the token valid or not in backend, we can do that using angular interceptors
we should create the new service with interceptors name
ng g service token-interceptor
In interceptor file we need to import the following
import { Injectable,Injector } from '#angular/core';
import { HttpInterceptor } from '#angular/common/http';
import { LoginService } from './login.service';
In interceptors services inject in different way compared to component.
export class TokenInterceptorService implements HttpInterceptor{
constructor(private Injector:Injector) { }
intercept(req:any,next:any)
{
let loginService = this.Injector.get(LoginService);
let tokenzedReq = req.clone({
setHeaders:
{
Authorization: `Bearer ${loginService.getToken()}`
}
});
return next.handle(tokenzedReq)
}
}
we need to create a function in interceptors with the name intercept, then we need to inject the service as per injector.
In backend we need to create the helper function to verify the jsonwebtoken
if the authorization not set we can send the response 401 not found and can redirected to login page
function verifyToken(req,res,next)
{
if(!req.headers.authorization)
{
return res.status(401).send('Unauthorized request');
}
var token = req.headers.authorization.split(' ')[1];
if(!token)
{
return res.status(401).send('Unauthorized request');
}
if(token === 'null')
{
return res.status(401).send('Unauthorized request');
}
//let payload = jwt.verify(token,'secretKey');
let payload = jwt.decode(token,'secretKey');
if(!payload)
{
return res.status(401).send('Unauthorized request');
}
req.userId = payload.subject;
next();
}
then we can use this middleware function wherever we need
for example
app.get('/dashboard',verifyToken,function(req,res){
let events = [];
res.json(events);
});
In dashboard component ts file
this.dashboardService.getData().subscribe(data=>this.dashboardData=data,
err=>{
if(err instanceof HttpErrorResponse)
{
if(err.status===401)
{
this.router.navigate(['/login']);
}
}
})
in dashboard service ts file
url = "http://localhost:3000/dashboard";
getData()
{
return this.http.get<any>(this.url);
}
in app.module.ts file
import { AuthGuard } from './auth.guard';
import { ReciptComponentComponent } from './recipt-component/recipt-component.component';
import { HttpClientModule, HTTP_INTERCEPTORS } from '#angular/common/http';
import { TokenInterceptorService } from './token-interceptor.service';
import { DashboardServiceService } from './dashboard-service.service';
in providers
providers: [AuthGuard,{provide:HTTP_INTERCEPTORS,useClass:TokenInterceptorService,multi:true},DashboardServiceService],
I'm trying to create a login application that passes a JWT in the headers of another application.
I started from this application User authentication keycloak 2 adapted to my Kecloak installation and it works fine.
Now I need to create a component that call an external URL passing the authorization token
In this component
import React, { Component } from 'react';
class callUrl1 extends Component {
constructor(props) {
super(props);
this.state = { response: null };
}
authorizationHeader() {
if(!this.props.keycloak) return {};
return {
headers: {
"Authorization": "Bearer " + this.props.keycloak.token
}
};
}
handleClick = () => {
console.log("callUrl1 called")
}
}
export default callUrl1;
I need something that call an external URL; something like:
SOME_FUNCTION('https://www.h.net/users', this.authorizationHeader())
to put inside handleClick.
All that I tried gives compilation errors.
How can I go from "http://localhost:3000" to "https://www.h.net/users" passing the JWT?
The built in library for making requests in frontend JS is called Fetch. Here's an example of how you might do it in your case:
handleClick = () => {
fetch('https://www.h.net/users', this.authorizationHeader())
.then((response) => {
// do something with the response here...
});
}
or with async/await:
handleClick = async () => {
const response = await fetch('https://www.h.net/users', this.authorizationHeader());
// do something with response like:
const data = await response.json();
}
For more information on Fetch, check out mdn: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
I have a NestJS backend, secured by JWT.
I would like to know what is the best way to store the actual user or the best way to pass it to my services?
I have a JwtAuthGuard
#Injectable()
export class JwtAuthGuard extends AuthGuard( 'jwt' ) {
canActivate(context: ExecutionContext) {
return super.canActivate( context );
}
handleRequest(err, user, info) {
if ( err || !user ) {
throw err || new UnauthorizedException();
}
return user;
}
}
My actual user id is in user var in handleRequest but I don't know where to "stock" it to be able to reach it in some modules.
Does anyone can help me ?
Thanks
The JWT itself is where you store the user id (or any identifying details of the user).
If you create the JWT payload with the user id ({ id: 123, ... }) the passport will set the user member to the request object.
Important: Don't store sensitive data in the JWT.
#AuthGuard( 'jwt' )
#Get('profile')
getUserId(#Request() req: any) {
return req.user.id;
}
You can pass the req.user.id to services as needed.
See: https://docs.nestjs.com/techniques/authentication#implement-protected-route-and-jwt-strategy-guards
One last thing:
If you like to have types for the request object you can do something like this
import { Request as HttpRequest } from 'express';
interface UserJwtPayload {
id: string,
}
type AuthRequest = HttpRequest & { user: UserJwtPayload }