how to handle token authentication in Angular Dart? - django-rest-framework

First time dealing with a SPA. I have a back-end restful service that returns a token when a user signs in. I know I am supposed to send the token through the headers in each request so I was thinking in saving the token in a file and create a service or a class that loads the token in every component but I don't know if this is a good approach as I can't find documentation for Angular Dart about this.

I saved the Token first in localStorage as Tobe O suggested:
Future login(username, password) async {
String url = 'http://127.0.0.1:8000/auth/login/';
var response =
await _client.post(url, body: {'username': username, 'password': password});
Map mapped_response = _decoder.convert(response.body);
window.localStorage.addAll({"token": mapped_response["key"]});
}
But still I was receiving 401 responses when I tried to get user information, this was the function:
Future check_authentification () async {
String _headers_key = "Authorization";
String _headers_value = "Token "+window.localStorage["token"];
var response = await _client.get("http://127.0.0.1:8000/auth/user/", headers: {_headers_key: _headers_value});
user_data = _decoder.convert(response.body);
response_status = response.statusCode;
}
I couldn't get authorized because django-rest-auth wasn't properly configured for token authorization. The solution was to add TokenAuthentication to the default authentication classes in django settings.
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
)}

Related

How to use two calls on the same session in flurl

I want to use GetAsync to get a token then to use that token in a POST function. Both Get and Post should be made on same session.
Here is the GET function:
var result = await SA_BaseUrl
.AppendPathSegments("ProdOrderConfSet")
.WithBasicAuth("UserName", "Password")
.WithHeader("Accept","application/json")
.WithHeader("X_CSRF_Token", "fetch")
.GetAsync();
IReadOnlyNameValueList<string> headers = result.Headers;
string X_CSRF_Token = headers.FirstOrDefault(h => h.Name == "x-csrf-token").Value;
And Post function:
var result2 = await SA_BaseUrl
.AppendPathSegments("ProdOrderConfSet")
.WithBasicAuth("UserName", "Password")
.WithHeader("Accept","application/json")
.WithHeader("Content-Type", "application/json")
.WithHeader("X_CSRF_Token", X_CSRF_Token)
.PostJsonAsync(workStep)
.ReceiveJson<DANTESAP_ProdWorkStep>();
Running the above code, I am getting:
403 Forbidden, CSRF token validation failed
How can I run the two functions on the same session?
Unfortunately, there is no documentation for the service I am trying to reach. But when I use Postman, it works without any problem. I do not know what is missing (or wrong) in my C# code.
Here is the Get function in order to get the token:
And here is the POST function that uses the extracted csrf token from the GET function:
Get the cookies and re-use them:
var result = await SA_BaseUrl
.AppendPathSegments("ProdOrderConfSet")
.WithBasicAuth("UserName", "Password")
.WithHeader("Accept","application/json")
.WithHeader("X_CSRF_Token", "fetch")
.WithCookies(out var jar)
.GetAsync();
...
var result2 = await SA_BaseUrl
.AppendPathSegments("ProdOrderConfSet")
.WithBasicAuth("UserName", "Password")
.WithHeader("Accept","application/json")
.WithHeader("Content-Type", "application/json")
.WithHeader("X_CSRF_Token", X_CSRF_Token)
.WithCookies(jar)
.PostJsonAsync(workStep)
.ReceiveJson<DANTESAP_ProdWorkStep>();

How to change an additional information of jwt access token

I'm working with Spring Security and use JWT as access token , When a client sends the access token to server I must change an additional information (metadata) of this token and return a new one.
How can I achieve that ?
i try with this code but not working
String authorization = Context.getHeader("Authorization");
if (authorization != null) {
String tokenValue = authorization.replace("Bearer", "").trim();
OAuth2AccessToken accessToken = tokenStore.readAccessToken(tokenValue);
accessToken.getAdditionalInformation().put("activeProfileId", defaultProfileId);
return accessToken.getValue();
}
return null;
You should get your metadata ("claims") from the token, then add them to a new JWT builder that will return a new token. The new JWT must be entered in HttpResponse to forward it to the client. Instead, the client will have to implement an interceptor to retrieve it in a comfortable and transparent way.
You've to get all Additional Information as HashMap and place them in OAuth2Authentication. stackoverflow.com/a/19057480/11951081
In ajax should be:
https://api.jquery.com/category/ajax/global-ajax-event-handlers/
$.ajaxSetup({
beforeSend: function (xhr) {
xhr.setRequestHeader('Authorization', <Jwt>)
},
success:function(event,jqXHR,ajaxOptions,data ){
console.log(ajaxOptions.getResponseHeader('Authorization'))
}
})

Django Rest Framework JWT Auth

I have an issue posting/getting any requests after JWT login. (I am using the djangorestframework-jwt library) The user succesfully logs in, the app returns the json token but when I use this token for the next requests I get
{
"detail": "Authentication credentials were not provided."
}
settings.py
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
),
}
JWT_AUTH = {
'JWT_ALLOW_REFRESH': True,
'JWT_EXPIRATION_DELTA': datetime.timedelta(hours=1),
'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=7),
}
Login View
def post(self, request, format=None):
if not request.data:
return Response({'Error': "Please provide username/password"}, status=status.HTTP_400_BAD_REQUEST)
username = request.data['username']
password = request.data['password']
try:
user = User.objects.get(email=username, password=password)
except User.DoesNotExist:
return Response({'Error': "Invalid username/password"}, status=status.HTTP_400_BAD_REQUEST)
if user:
payload = {
'id': user.id,
'email': user.email,
'first_name': user.first_name
}
jwt_token = jwt.encode(payload, "SECRET_KEY") # to be changed
return Response({'token': jwt_token}, status=status.HTTP_200_OK)
and then every other view contains
authentication_class = (JSONWebTokenAuthentication,)
permission_classes = (IsAuthenticated,)
I have tested both with postman and curl but I get the same error. Not sure if there is an error with the header format or if there is smth else I'm missing. Any help would be greatly appreciated!
EDIT:
I have changed my settings to
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
),
but now I get that
{
"detail": "Error decoding signature."
}
EDIT: I think the issue is that jwt_token = jwt.encode(payload, 'SECRET_KEY') might return a token that is not recognised...if i use the token generated by obtain_jwt_token then i can query any endpoint. Could anyone explain this?
EDIT: so I changed to jwt_token = jwt_encode_handler(payload) and the settings file contains the JWT_SECRET_KEY (when i verify the token i receive after login on jwt, it is indeed the right token with the right payload and secret) but it's still not recognised "detail": "Invalid signature."
I managed to solve my problem. When authenticating a user I was checking against a custom user table that I had created earlier, which was different from the django's auth_user table. I changed django's settings to use my custom users table and then using the token from the authentication worked for the other requests as well.
AUTH_USER_MODEL = 'main.User'
The problem is you encode the JWT by using "SECRET KEY" and decode using another key. The default secret key used by jwt is settings.SECRET_KEY. But when you create a custom jwt token you uses a string as secret "SECRET_KEY". But you use default jwt verification which automatically uses settings.SECRET_KEY.
So add 'JWT_SECRET_KEY': settings.SECRET_KEY, in to your JWT settings and use this key for encode and decode.
Ex:
jwt_token = jwt.encode(payload, settings.SECRET_KEY)
or you can override the jwt verification and create your own.

Adding the application autehntication to Swagger UI

I have Spring boot app where I integrate JWT authentication.
In order to authenticate, user need to send a POST request to /login with username and password, then he gets a JSON response with {"token": "BEARER SOME_TOKEN" }.
In my swagger UI, when I click "Try it out" and execute a request, the request is being sent without any token.
Question - Is there any way to query a login request and add authorization token to the Swagger UI requests?
In this case we can intercept the tokens and add them to all the requests by using these two interceptors in the index.html when defining SwaggerUIBundle:
const ui = SwaggerUIBundle({
...
responseInterceptor:
function (response) {
if (response.obj.access_token) {
console.log(response.obj.access_token)
const token = response.obj.access_token;
localStorage.setItem("token", token)
}
return response;
},
requestInterceptor:
function (request) {
request.headers.Authorization = "Bearer " + localStorage.getItem("token");
return request;
}
}
The responseInterceptor captures the response and if it contains the field "token" it saves it in local storage.
The requestInterceptor adds the Authorization header on each call you make from swagger-ui using the value from local storage.
This fix is for using v3 of swagger-ui:
<script src="https://unpkg.com/swagger-ui-dist#3.12.1/swagger-ui-standalone-preset.js"></script>
<script src="https://unpkg.com/swagger-ui-dist#3.12.1/swagger-ui-bundle.js"></script>

How to know that access token has expired?

How should client know that access token has expired, so that he makes a request with refresh token for another access token?
If answer is that server API will return 401, then how can API know that access token has expired?
I'm using IdentityServer4.
Your api should reject any call if the containing bearer token has already been expired. For a webapi app, IdentityServerAuthenticationOptions will do the work.
But your caller Web application is responsible for keeping your access_token alive. For example, if your web application is an ASP.Net core application, you may use AspNetCore.Authentication.Cookies to authenticate any request. In that case, you can find the information about the token expiring info through OnValidatePrincipal event.
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationScheme = "Cookies",
//ExpireTimeSpan = TimeSpan.FromSeconds(100),
AutomaticAuthenticate = true,
AutomaticChallenge = true,
Events = new CookieAuthenticationEvents()
{
OnValidatePrincipal = async x =>
{
if (x.Properties?.Items[".Token.expires_at"] == null) return;
var now = DateTimeOffset.UtcNow;
var tokenExpireTime = DateTime.Parse(x.Properties.Items[".Token.expires_at"]).ToUniversalTime();
var timeElapsed = now.Subtract(x.Properties.IssuedUtc.Value);
var timeRemaining = tokenExpireTime.Subtract(now.DateTime);
if (timeElapsed > timeRemaining)
{
//Get the new token Refresh the token
}
}
}
}
I have added a full implementation about how to get a new access token using refresh token in another StackOverflow answer

Resources