When making a request to a flask route that requires a JWT to access using (#jwt_required decorator on flask-restful resources), I get a 422 UNPROCESSABLE ENTITY with the message: The specified alg value is not allowed.
When logging in and navigating to the (frontend) route that calls the request:
this.axios.get("/jobs").then(res => {
console.log(res.data);
this.jobs = res.data.jobs;
});
in the same go, it works as expected, however on refresh it then shows the 422 error.
I store the token in localstorage and load it into axios headers like so:
const api = {
init: function() {
Vue.use(VueAxios, axios);
Vue.axios.defaults.baseURL = DEV_URL;
},
setHeader: function() {
const token = `Bearer ${getToken()}`;
Vue.axios.defaults.headers.common["Authorization"] = token;
},
};
and call init() and setHeader() in my main.js so I am confused why this is causing an error only after a refresh.
I haven't be able to find any resources on how to remedy the The specified alg value is not allowed error. Any assistance would be appreciated! :)
I ran into same problem when the JWT token was created in my spring boot auth service but resource service was a flask micro service. I tried the following steps to sort it out,
I pasted the token in jwt.io Debugger.
On the right hand side I found the decoded header where the alg value was the following,
{
"alg": "HS512"
}
I put the alg in the app config in the flask resource server as follows,
app.config['JWT_ALGORITHM'] = 'HS512'
After that the error message was gone and I was able to parse information from the decoded token. So you need to find the algorithm by which the token was generated and set the appropriate algorithm in the flask app.config.
This can happen if the token was created using a different algorithm then app.config['JWT_ALGORITHM']
Related
I am building an application. The client is built with Next.js and the backend with Django and Django REST framework.
In this application, I would like to have social login.
So far, my situation is this.
I have set up the OAuth on the Google dashboard
On the client, I am using next-auth - The client is successfully calling Google and getting an access token from there.
On the client, the callback that runs after getting the access token from Google makes a call my Django API.
I have set up the backend with dj_rest_auth - My settings are almost identical to the ones described here.
Once the client callback runs and calls my Django API with the access token from Google, I successfully get on the client an access token and a refresh token.
If it is a new user loggin in the first time, a new user is created in Djangos DB
const response = await fetch(`${djangoAPIurl}/api/social/login/google/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
access_token: accessToken,
id_token: idToken
})
});
const data = await response.json();
const { access_token, refresh_token } = data;
Both access_token and refresh_token are defined and appear to be valid tokens.
So far, everything happens as expected. My issue appears after this point.
In my api, I have another view defined.
#api_view(['GET'])
#authentication_classes([SessionAuthentication, BasicAuthentication, TokenAuthentication])
#permission_classes([IsAuthenticated])
def test_view(request):
current_user = request.user
print('current_user.auth: ', current_user.is_authenticated)
response = JsonResponse({"received": True})
return response
From my client, I am attempting to call this view in the following way.
const response = await fetch(`${djangoAPIurl}/api/test/test_view/`, {
headers: new Headers({
Authorization: `Bearer ${session.accessToken}`
})
});
The header is constructed correctly, with session.accessToken being the value I got from the api/social/login/google/ call and the request is routed correctly, however, it fails with Forbidden 403 because the user is not authenticated. I have removed the authentication and permission decrators and the request ends up being processed by the view, and there, upon inspection of the user, it is an Anonymous user. I have also tried changing Bearer to Token, to no avail.
Do you have any advice what I might be doing wrong or missing? Have I completely missunderstood how to use the token I get back from api/social/login/google/? All advice is much appreicated!
I think this is because your secret for hashing JWTS on the client side and server side is not same. Next-Auth automatically creates a secret key for hashing jwt's and dj_rest_auth does the same, unless you explicitly tell them both to use the same secret for hashing jwts. I'm a bit late to answer this, but Hope this will help future people😁😁.
I am trying to build an application using VueJS and Django. I am also using Graphene-Django library, as the project utilize GraphQL.
Now, The authentication works fine and i get a JWT Token back.
But when i use the token for other queries that need authentication, i got this error in Vue:
"Error decoding signature"
and the Django Log also returns this:
graphql.error.located_error.GraphQLLocatedError: Error decoding signature
jwt.exceptions.DecodeError: Not enough segments
ValueError: not enough values to unpack (expected 2, got 1)
the bizarre thing is that the same query when executed in Postman just works fine.
As i mentioned in the title is use Axios for my requests, here's an example of a request:
axios({
method: "POST",
headers: { Authorization: "JWT " + localStorage.getItem("token") },
data: {
query: `{
dailyAppoint (today: "${today}") {
id
dateTime
}
}`
}
});
Note: It uses 'JWT' not 'Bearer' because somehow 'Bearer' didn't work for me.
Ok, couple of questions, does you API work without Vue.js from curl. Generate token, check API from curl.
If it does, then check the Headers sent from the request, from Network Inspector, mozilla dev tools/chrome devtools. And update your Post with those RAW Headers.
This particular error arises when your public key is unable to decode the string[token] signed by your private key. Which ultimately means the access token has been tampered with. It could also mean you're sending values like 'unkown'-- JS state initialization error.
Check the RAW headers of the request. It'll help.
Use a request interceptor to set the Authorization header:
axios.interceptors.request.use(config => {
if (localStorage.getItem("token") != null)
config.headers["Authorization"] = "JWT " + localStorage.getItem("token");
return config;
});
Background
Using Firebase Auth and a SAML Auth Provider with the following configuration:
const config = {
apiKey: "AIzaSy...",
authDomain: "example-app.firebaseapp.com",
};
firebase.initializeApp(config);
const provider = new firebase.auth.SAMLAuthProvider('saml.example-idp');
function saml() {
firebase.auth().signInWithRedirect(provider)
.then((result) => {
console.log(result);
})
.catch((error) => {
console.log(error);
});
}
The CICP configuration for the SAML upstream has the Service Provider: our entity id and the ACS configured as our CICP https://example-app.firebaseapp.com/__/auth/handler.
What I expect to happen
To be able to set a breakpoint in the signInWithRedirect()'s Promise's then and see the authenticated user.
What actually happens
Flow is redirected to the IdP and authentication handled.
The IdP emits the redirect-posting page with automatic submit-on-load and a multipart/form-data form with:
Content-Disposition: form-data; name=SAMLResponse - containing base64
encoded signed SAMLResponse
Content-Disposition: form-data;
name=RelayState - containing the relay state from the SAML flow
Content-Disposition: form-data; name="ssourl" - containing the
firebase project auth handler URI
This in turn causes CICP to render and return a page with script that sets up
var POST_BODY=""------WebKitFormBoundary9bn7AOpnZiIRk9qZ\r\nContent....."
i.e. rather than parsing the form body and extracting the SAMLResponse field, it is replaying the entire Request.body into the script and then calling fireauth.oauthhelper.widget.initialize();
This obviously fails because that roundtrips and then attempts to post the entire response body to the /__/auth/handler endpoint as a querystring.
I suspect there's a simple configuration item that's missing from this chain, because all of the flows look normal to me until the multipart form data gets pushed into the POST_BODY and then prevents the transform of the SAML token into an OAuth token.
The question
What configuration item is incorrect in this (redacted) setup, and what is the correct derivation of the value to replace it with?
Maybe there is also an additional technical issue with SAML, but at least there's a incoherence point in the way sign-in method is used.
According to (Official Docs)[https://cloud.google.com/identity-platform/docs/how-to-enable-application-for-saml#handle-signin-with-client-sdk], you have 2 options to sign-in :
1) With Popup
In this case, you can use a promise to retrieve user credential with sign-in method:
firebase.auth().signInWithPopup(provider)
.then((result) => {
// User is signed in.
// Identity provider data available in result.additionalUserInfo.profile,
// or from the user's ID token obtained via result.user.getIdToken()
// as an object in the firebase.sign_in_attributes custom claim
// This is also available via result.user.getIdTokenResult()
// idTokenResult.claims.firebase.sign_in_attributes.
})
.catch((error) => {
// Handle error.
});
2) With Redirect
In this case, your code should be split into 2 parts.
First sign-in method, without using any promise :
firebase.auth().signInWithRedirect(provider);
Then, the initialization of a "listener", to retrieve user credential after the sign-in redirect :
// On return.
firebase.auth().getRedirectResult()
.then((result) => {
// User is signed in.
// Identity provider data available in result.additionalUserInfo.profile,
// or from the user's ID token obtained via result.user.getIdToken()
// as an object in the firebase.sign_in_attributes custom claim
// This is also available via result.user.getIdTokenResult()
// idTokenResult.claims.firebase.sign_in_attributes.
})
.catch((error) => {
// Handle error.
});
To be added in the bootstrap part of your page/app.
Hope it will help.
Short answer to long question:
The SAML provider handling in Firebase Auth and Google CICP doesn't process multipart/form-data and needs to be in application/x-www-form-urlencoded.
This is a SAML IdP configuration, not something which can be handled by the Firebase Auth service provider configuration.
I am using an auth API using JWT and it works great.
This API is being used to authorize users for my web app. For this to work, I store JWT access_tokens as cookie manually with Flask.
I secure my resource with #JWT_required decorator and if I try to access a secure resource with a valid token everything works fine.
However, if the access token is missing or invalid/expired I get a JSON saying:
{
"message": "Missing cookie \"access_token_cookie\""
}
This is obvious the right message but rather then showing a JSON I want to redirect to the appropriate statuscode error page that is provided by Flask - in this case 401.
I have tried adding error handling for Flask and JWT Manager
Custom decorator, although I have played only poorly with this as I believe there has to be solution within FLASK-JWT-extended
#app.route('/dashbord')
#jwt_required
def dashbord():
return render_template('dashbord.html', title='Home')
My goal is to redirect to appropriate error page 404, 403, 401 if anything is wrong with the access token.
THE SOLUTION:
#jwt.unauthorized_loader
def my_invalid_token_callback(expired_token):
return render_template('401.html', title='Home')
Here's the solution Benjo posted at the bottom of his question:
#jwt.unauthorized_loader
def my_invalid_token_callback(expired_token):
return render_template('401.html', title='Home')
Here is the documentation for changing the results for invalid tokens: https://flask-jwt-extended.readthedocs.io/en/stable/changing_default_behavior.html#changing-callback-functions
Am trying to write custom middleware in the ASP.net core pipeline, as part of my invoke, would like to append/add cookie, so then next middleware in the pipeline can access those cookie.
getting compiling error on set the cookie value. Can anyone recommend work around for this.
Note: When I tried with Response.Cookie , it works but only problem is, cookie is reflecting only on next request from the browser, but I need this to be reflecting on the next middleware in the pipeline immediately after execute this.
below code snippet
public async Task Invoke(HttpContext httpContext)
{
var queryParameters = httpContext.Request.Query;
var cookies = httpContext.Request.Cookies;
if (!cookies.ContainsKey(".AspNetCore.Session")
|| cookies[".AspNetCore.Session"] != "new_key")
{
httpContext.Request.Cookies[".AspNetCore.Session"] = "new_key";
}
await _next.Invoke(httpContext);
}
You cannot use cookie's value in same request. However, you could use good old HttpContext.Items.
public async Task InvokeAsync(HttpContext context)
{
context.Request.HttpContext.Items["key"] = "Hello!";
await _next(context);
}
You then retrieve it as
var value = HttpContext.Items["key"];
In my case I have an AuthorizationHandler that performs some checks to determine the user details and whether the user is logged in. The auth handler stores some of this info in a token in the request headers, so it can be easily accessed by the controllers.
When the user is logged in, this token can be read from the HttpContext.Request.Headers in a standard controller and all is well.
When the user is not logged in, the auth handler returns failure and so the request is redirected to "/login". Sadly the token header is not preserved across the redirect, so in my LoginController the token is null.
The only way I could make the token available to both a standard controller and LoginController is to store the token in both the request headers AND response cookies. This cookie can be read from the LoginController in the HttpContext.Request.Cookies collection. I set it to be short-lived as it's only needed briefly (it'll disappear after 5 seconds)
Here is part of the code from my auth handler:
HttpRequest request = _httpContextAccessor.HttpContext.Request;
HttpResponse response = _httpContextAccessor.HttpContext.Response;
request.Headers["X-Token"] = encryptedToken;
response.Cookies.Append("TokenCookie", encryptedToken, new CookieOptions
{
MaxAge = TimeSpan.FromSeconds(5),
Secure = true,
IsEssential = true,
});