Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 1 year ago.
Improve this question
My angular app connects to an AdonisJS rest api thatrequires an auth token to work. This token is generated on succesful login however storing it for future requests is a problem. Ive tried to use window.sessionStorage but unfortunately this remains undefined until the user refreshes the page. Which means my angular app doesent get the token and makes the app not to work until the page is refreshed. Any suggestions or alternatives??
An example:
First, in your auth.service.ts, 2 methods to store/get the token
// STORE the token in localstore:
setToken(token:string){
// First, serialize it (but just if token is not string type).
const tokenString:string = JSON.stringify( token );
localStorage.setItem('token', tokenString);
}
// READ the token from localstorage and Deserialize
getToken(): string | null{
let token = localStorage.getItem( 'token' );
if( token !=null){
// You just need to parse if you serialized it inside setToken() method
token = JSON.parse(carItemsString);
}
return token;
}
Then, in your interceptor:
import { Injectable } from '#angular/core';
import {
HttpRequest,
HttpHandler,
HttpEvent,
HttpInterceptor,
} from '#angular/common/http';
import { AuthService } from '../_services/auth.service';
#Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(private authService: AuthService) {}
intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
const url="\yourAPI\endpoint";
// Get your token
cont myToken = this.authService.getToken();
// Add authorization header with token if available
if (myToken) {
request = request.clone({
setHeaders: {
Authorization: `Bearer ${currentUser.user.api_token}`,
'Content-Type': 'application/json',
},
url,
});
}
...
}
More info about how to Adding and updating headers HERE
More info about how to Use the interceptor for Intercepting requests and responsesHERE
You can store your token using localStorage or sessionStorage depending on your requirements (Session storage vs localStorage on paragraph 1
If you are using Angular to make the subsequent requests, consider creating an HttpInterceptor that is responsible from reading the token from localStorage and puting it in the HTTP Header of the requests.
Related
I'm new to web development and am currently stucked at a problem I can't solve easily. I'm using Django3.2.6, django restframework (DRF) 3.14, vue3.0 and axios (to make API calls). I wrote an APIView to lock a model while editing an it's instance:
class LockCmAPI(APIView):
def post(self, request, **kwargs):
obj = get_object_or_404(CM, id=self.kwargs['pk'])
obj.lock()
print('locking object')
return HttpResponse(status=status.HTTP_200_OK)
For the frontend I created a Vue app that calls periodically my LockCmAPI to lock the instance and prevent others from editing it:
let vue = Vue.createApp({
delimiters: ['[[', ']]'],
data: function(){
return{
current_cm: cm_obj,
intervall: null,
}
},
methods: {
lockCmHeartbeat(){
console.log('locking');
console.log(`${BACKEND_PATH+LOCK_PATH+this.current_cm.id}/`);
axios.post(`${BACKEND_PATH+LOCK_PATH+this.current_cm.id}/`, this.current_cm, {
xsrfCookieName: 'csrftoken',
})
.then((response) => {
console.log('lock');
console.log(response);
});
}
},
mounted() {
this.lockCmHeartbeat();
this.intervall = setInterval(function(){
this.lockCmHeartbeat();
}.bind(this), FIVE_SEC_IN_MILISEC);
},
beforeDestroy() {
clearInterval(this.interval);
}
});
vue.mount('#cm_vue_block');
After running my code I get a 403 response with the message "Request failed with status code 403". When I looked further into the response I got this "{\"detail\":\"CSRF Failed: CSRF token missing or incorrect.\"}" in my responseText.
My Question:
Why does it tell me I sent an incorrect csrftoken since it's the same csrftoken in the cookie named csrftoken?
Can someone clarify it for me?
How can I fix this problem?
THX :D
For everyone who is going to have the same problem. Since the csrftoken I provided is exactly the same as the csrftoken I saw in my cookie named csrftoken. It had to be another issue...After reading the django documentation https://docs.djangoproject.com/en/4.1/ref/settings/#std-setting-CSRF_HEADER_NAME :
Default: 'HTTP_X_CSRFTOKEN' The name of the request header used for
CSRF authentication.
As with other HTTP headers in request.META, the header name received
from the server is normalized by converting all characters to
uppercase, replacing any hyphens with underscores, and adding an
'HTTP_' prefix to the name. For example, if your client sends a
'X-XSRF-TOKEN' header, the setting should be 'HTTP_X_XSRF_TOKEN'.
I realized my csrf headername is named different to djangos default CSRF_HEADERNAME. In order to solve this problem I configured xsrfHeadername in my axios request, which looks like this:
axios.post(`${BACKEND_PATH + LOCK_PATH + this.current_cm.id}/`, this.current_cm, {
xsrfCookieName: 'csrftoken',
xsrfHeaderName: 'X-CSRFTOKEN',
})
I've searched around a bit, but have not found any clear, up-to-date, answers on this topic.
I'm trying to implement JWT authentication in my NextJS application. The following is what I have so far.
/login endpoint that will (1) check that the user/pass exists and is valid, and (2) create a JWT token based on a private RS256 key.
Created a middleware layer to verify the JWT
The creation of the JWT is fine - it works perfectly well reading the key from the file-system and signing a JWT.
However, I've run into the problem of the middleware being unable to use node modules (fs and path) because of the edge runtime (read here). This makes it so I'm unable to read the public key from the FS.
What is the proper way to verify a JWT token on every request? I've read that fetching from middleware is bad practice and should be avoided. All other reference on this topic (that I found) either uses a "secret" instead of a key (and can therefor be put into process.env and used in middleware) or glosses over the fact (1). Or should I just create a separate express application to handle JWT creation/verifying?
What I do is add a file (_middleware.tsx) within pages. This ensures the file runs each page load but is not treated as a standard page. In this file lives an Edge function. If the user is not signed in (no JWT in cookies) and tries to access a protected page, he is immediately redirected to /signin before the server is even hit.
import { NextResponse } from "next/server";
const signedInPages = ["/admin", "/members "];
export default function middleware(req) {
if (signedInPages.find((p) => p === req.nextUrl.pathname)) {
const { MY_TOKEN: token } = req.cookies;
if (!token) {
return NextResponse.redirect("/signin");
}
}
}
If you signed the token from
import jwt from "jsonwebtoken";
then you can use same jwt for verifying token inside a middleware function. you create _middleware.js inside pages directory and middleware will run for each request before request hits the endpoint:
import { NextResponse } from "next/server";
import jwt from "jsonwebtoken";
export async function middleware(req, ev) {
const token = req ? req.cookies?.token : null;
let userId=null;
if (token) {
// this is how we sign= jwt.sign(object,secretKey)
// now use the same secretKey to decode the token
const decodedToken = jwt.verify(token, process.env.JWT_SECRET);
userId = decodedToken?.issuer;
}
const { pathname } = req.nextUrl;
// if user sends request to "/api/login", it has no token. so let the request pass
if (
pathname.includes("/api/login") || userId
) {
return NextResponse.next();
}
if (!token && pathname !== "/login") {
return NextResponse.redirect("/login");
}
}
you send the token in the post request header.
{Authorization: 'Bearer ' + token}
And then verify that token on the server, before sending data back
To verify the token you create a middleware function, then use that function on any route you want to portect
router.get("/", middlewarefunction, function (req: any, res: any, next: any) {
res.send("/api")
})
I would like to implement auto refresh jwt token before every request to GraphQL with Apollo middleware in React Native app. After every user login he gets two tokens: access and refresh. Access token it is the short one for 30-60 min for using in authorization header. And refresh token it is the long one for 60 days for confirm of refresh token graphql mutation.
My flow:
User login and gets 2 tokens -> put access token to authorization header with Appollo setContext.
User make request to GraphQL -> check expireTime of accessToken on a client side:
-> if it is not expired -> confirm request
-> if it is has expired -> call GraphQL refreshToken mutation -> get new tokens -> confirm request.
For keeping tokens on the client side i use KeyChain storage. Can you tell me please should i use Apollo cache for keeping tokens too? Should i write Apollo state for tokens? And how i can implement my flow?
GraphQL mutation
mutation UpdateTokens($refreshToken: String!, $refreshTokenId: String!)
{
updateTokens(refreshToken: $refreshToken, refreshTokenId: $refreshTokenId) {
user {
name
phone
}
accessToken
refreshToken
}
}
App.js
import React from 'react'
import { ApolloClient } from 'apollo-client'
import { ApolloLink } from 'apollo-link'
import { ApolloProvider } from 'react-apollo'
import { ApolloProvider as ApolloHooksProvider } from 'react-apollo-hooks'
import { createHttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { setContext } from 'apollo-link-context'
import * as Keychain from 'react-native-keychain'
import AppNavigator from './AppNavigator'
const httpLink = createHttpLink({
uri: 'http://localhost:4000'
})
const cache = new InMemoryCache()
const authLink = setContext(async (req, { headers, ...context }) => {
const tokens = await Keychain.getGenericPassword()
const accessToken = tokens.username
return {
headers: {
...headers,
authorization: accessToken ? `Bearer ${accessToken}` : ''
},
...context
}
})
const client = new ApolloClient({
link: ApolloLink.from([authLink, httpLink]),
cache,
connectToDevTools: true
})
const App = () => {
return (
<ApolloProvider client={client}>
<ApolloHooksProvider client={client}>
<AppNavigator />
</ApolloHooksProvider>
</ApolloProvider>
)
}
export default App
I honestly think you are on the right path -- as I read your required functionality I thought of apollo-link-context and was happy to see you were taking that approach. We recently had to implement similar functionality in a react-native app, which entailed attaching custom headers with authentication-related data. To retrieve this data we were required to make an async request, although ours was over the network to a third-party service. We did this all in setContext from the client just as you are. This worked well.
I do not think you need to concern yourself with updating your Apollo cache with your tokens, manually or otherwise, for at least a few reasons. First, given the quasi-sensitive nature of what you are storing, it would be best practice to defer to the more secure storage solution, which in this case is likely the Keychain. In addition, having a single source of truth for the tokens in your application can keep things clean.
Assuming you do not want to write this data to cache, I would double-check Apollo client is not automatically doing so. For example, if you for some reason previously queried your token data, Apollo might automatically update your cache upon receiving the mutation payload. Just something to be mindful of.
I am able to get the access token but unable to use in a constructor method in angular service. console.log just prints the token but it is not getting assigned to a variable that I need to do.
Portion of my code.
constructor(private http: HttpClient) {
// get the access_token via Node JS
this.http.get(this.apiUrl + '/api/getToken').subscribe(d=>{
this.token = JSON.stringify(d);
console.log(this.token);
})
}
If you need to use the token in the constructor, make sure it is already received.
constructor(private http: HttpClient) {
this.http.get(this.apiUrl + '/api/getToken').subscribe(d=>{
this.token = JSON.stringify(d);
console.log(this.token);
// do your stuff
// or invoke your method
})
}
Or if you want to perform some specific stuff with the token in another method myMethod, try this
myMethod(private http: HttpClient) {
this.http.get(this.apiUrl + '/api/getToken').subscribe(d=>{
this.token = JSON.stringify(d);
console.log(this.token);
// some operation, involved with token
})
}
In this case, it is ensured that the token is retrieved and available before using it.
I downloaded the 19-auth sample and add some console.log debug code to it, then found some problems.
The code in JwtAuthGuard is never executed: '2222222' was not printed to the console in the code below:
canActivate(context: ExecutionContext) {
console.log('22222222222');
// add your custom authentication logic here
// for example, call super.logIn(request) to establish a session.
return super.canActivate(context);
}
When I changed the guard to JwtAuthGuard in the AuthController:
#get('data')
#UseGuards(JwtAuthGuard)
findAll(#Req() req) {
return req.user;
// this route is restricted by AuthGuard
// JWT strategy
}
the code in JwtAuthGuard was invoked, but in the canActivate function, I can't get the user info from request. and the canActivate function was called before the JwtStrategy?
Can someone explain how the code executing for the auth module, and how to get the user info in the JwtAuthGuard?
paste the latest code and console log here:
JwtStrategy
/**
* jwt passport 调用validate方法来判断是否授权用户进行接口调用
* #param payload
*/
async validate(payload: AuthPayload) {
Logger.log(`payload is ${JSON.stringify(payload)}`, 'JwtStrategy');
const user = await this.authService.validateUser(payload.id);
if (!user) {
throw new UnauthorizedException('不存在的用户信息');
}
return user;
}
JwtAuthGuard
canActivate(context: ExecutionContext) {
// add your custom authentication logic here
// for example, call super.logIn(request) to establish a session.
// this.accessPriv = this.reflector.get<string>('accessPriv', context.getHandler());
console.log('canActivate executed 111111111111111111');
return super.canActivate(context);
}
and the console log as below:
canActivate executed 111111111111111111
[Nest] 14080 - 2019-04-01 11:19 [JwtStrategy] payload is {"userName":"fanliang","id":"1","iat":1553772641,"exp":1554377441} +2286ms
it seems that the canActivate() function of JwtAuthGuard executed before the validate() function of JwtStrategy, but the user info was attached to the request after JwtStrategy validate().
what I want is to get the user info from request in the canActivate() of custom AuthGuard such like JwtAuthGuard
I have a somewhat solution that works for me.
Calling the super.canActivate before my own logic.
seems like the population of req.user triggered by it.
An example:
import { ExecutionContext, Injectable } from "#nestjs/common";
import { AuthGuard } from "#nestjs/passport";
import { Request } from "express";
#Injectable()
export class AuthGuardWithAllowSentry extends AuthGuard("jwt") {
public async canActivate(context: ExecutionContext) {
// that code will call the passport jwt
const origCanActivate = await super.canActivate(context);
// now we have request.user!
const http = context.switchToHttp();
const request = http.getRequest<Request>();
console.log(request.user)
if (request.header("X-Sentry-Token") === "blablabla") {
if (request.method === "GET" && request.path.endsWith(".map")) {
return true;
}
}
// some random logic
return request.user.roles.includes("admin")
}
}
it feels for me more like a workaround than a real thing.
I agree that the 19-auth sample is a little bit confusing to follow. This is mainly because it includes the JWTAuthGuard (as a reference for building custom guards) but it is never actually used. Instead, the original use of plain AuthGuard is already set up to provide JWT functionality. However, both guards leverage the JWTStrategy. If you want to understand this better, you could try updating your AuthController:
#Get('data')
#UseGuards(AuthGuard())
findAll() {
// this route is restricted by AuthGuard
// JWT strategy
return {
message: 'Successfully passed AuthGuard',
};
}
#Get('custom-jwt')
#UseGuards(new JwtAuthGuard())
// this route is restricted by JWTAuthGuard custom
// JWT strategy
customJwt() {
return {
message: 'Successfully passed JWTAuthGuard',
};
}
The important part is that in order to get past either guard, you must send the request with the Authorization header properly set to the token that's returned from the token endpoint.
For example: Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InRlc3RAZW1haWwuY29tIiwiaWF0IjoxNTU0MDUyNDczLCJleHAiOjE1NTQwNTYwNzN9.3Q8_FC-qFXk1F4KmMrHVSmmNGPAyHdt2myr5c18_E-U
I find it easiest to use a tool like Postman or Insomnia for constructing requests and setting Headers, etc but you could also use CURL. Once you've set the Authorization header with a valid token you'll be able to hit both of the guarded endpoints. If you put a console.log in the JWTStrategy you'll see that both guards end up using the validate method to retrieve the user correctly.