I've been looking all around for session based authentication with Angular 2.
I'm building an application that has Django on backend and Angular 2 on the frontend. To keep the process simple I'm trying to implement Django session authentication.
// Angular 2 authentication service
import { Injectable } from "#angular/core";
import { Headers, Http, Response } from "#angular/http";
import "rxjs/add/operator/toPromise";
import 'rxjs/add/operator/map'
import { AppSettings } from "../../app.settings";
#Injectable()
export class UserAuthService {
private headers = new Headers({'Content-Type': 'application/json'});
private loginUrl = `${AppSettings.BACKEND_URL}` + '/api/v1/users/login/';
constructor(
private http: Http
) { }
login(username, password) {
let data = {
username: username,
password: password
};
return this.http.post(this.loginUrl, data, this.headers)
.map((response: Response) => response.json());
}
}
# Django Login view
def login(self, request):
username = request.data['username']
password = request.data['password']
user = authenticate(username=username, password=password)
if user is not None:
login(request, user)
serializer = self.serializer_class(user)
return Response(serializer.data, status=status.HTTP_200_OK)
raise AuthenticationFailed
I'm successfully calling backend API and my login view returns the successful response.
Also request.user gets updated after the login but when I try to call the other APIs using Angular or directly browse Django rest API user is not logged in.
The answer to this question is to append CSRF token to the X-CSRF header, because django uses X-CSRF token header to verify the sessions.
I don't exactly remember where I saw this but Iachieved this by using angular2-cookie and writing a custom request options service like this
// Custom request options service
import { CookieService } from "angular2-cookie/services/cookies.service";
import { Headers, RequestOptions } from "#angular/http";
import { Injectable } from "#angular/core";
#Injectable()
export class CustomRequestOptionsService {
constructor(
private cookieService: CookieService
) { }
defaultRequestOptions() {
return new RequestOptions({
headers: new Headers({
'Content-Type': 'application/json',
}),
withCredentials: true
});
}
authorizationRequestOptions() {
return new RequestOptions({
headers: new Headers({
'Content-Type': 'application/json',
'X-CSRFToken': this.cookieService.get('csrftoken')
}),
withCredentials: true
});
}
}
and then in your service where you hit secure APIs use it like this
// Officer service
import { Http, Response} from "#angular/http";
import { Injectable } from "#angular/core";
import "rxjs/add/operator/map";
// Services
import { CustomRequestOptionsService } from "../shared/custom-request-options.service";
#Injectable()
export class OfficerService {
private officerDashboardUrl = `http://${process.env.API_URL}` + '/api/v1/officers/detail';
constructor(
private http: Http,
private customRequestOptionService: CustomRequestOptionsService
) { }
getOfficer(officerId: number) {
return this.http.get(`${this.officerDashboardUrl}/${officerId}/`,
this.customRequestOptionService.authorizationRequestOptions())
.toPromise()
.then((response: Response) => {
return response.json();
})
.catch((error: any) => {
return Promise.reject(error.message || error)
});
}
}
Related
Below are the two files LoginScreen.JS which has a submit handler that submits the input. Here we import the axios instance from login.JS. I have also attached the same working example from PostMan.
login.js
const baseURL='http://127.0.0.1:8000/';
const axiosInstance = axios.create({
baseURL: baseURL,
timeout: 5000,
headers: {
'Content-Type': 'application/json',
accept: 'application/json'
},
});
export default axiosInstance
LoginScreen.js
const submitHandler = (e) => {
e.preventDefault()
axiosInstance
.post(`auth/token/`,{
username: email,
password: password,
grant_type: 'password',
client_id: 'Vqvt1yp2HahF8KgwOS3BrWaCUX8ViDGCrn3VfJkz',
client_secret: 'Vqvt1yp2HahF8KgwOS3BrWaCUX8ViDGCrn3VfJkz'
})
.then((res) => {
localStorage.setItem('access_token', res.data.access);
localStorage.setItem('refresh_token', res.data.refresh);
});
};
views.py
class DeviceView(viewsets.ModelViewSet):
serializer_class = DevicesSerializer
queryset = Devices.objects.all()
permission_classes = [permissions.IsAuthenticated]
Axios Request
axios.delete(`api/devices/${data.id}/`, {}, {
headers: {'X-CSRFToken': csrftoken }
})
.then((response) => {
console.log(response);
}).catch((error) => {
console.log(error);
});
When I carry out this request on my front-end, I get a response of :"DELETE http://localhost:3000/api/devices/4/ 403 (Forbidden)". Where 4 is the ID belonging to the record I would like to delete.
I am currently using Session Authentication in Django and I have passed in my CSRF Token value into the header of my request.
When I use other methods like PUT and POST on forms, they work fine. But, not DELETE
What am I doing wrong to receive this error?
Turns out I just needed to remove the empty body in the Axios request.
axios.delete(`api/devices/${data.id}/`, {
headers: {'X-CSRFToken': csrftoken }
})
.then((response) => {
console.log(response);
}).catch((error) => {
console.log(error);
});
I have my backend in Django and front in Vue.
A user performes login in Vue and via a POST request the creds are sent to a Django JWT login endpoint. This endpoint returns a token which is set in localStorage.
Then I want to check in Vue that the user is logged in. For that another endpoint in Django exists. However, it always returns "AnonymUser". I cannot get how to set this check.
Django:
My settings.py
JWT_AUTH = {
'JWT_ALLOW_REFRESH': True,
'JWT_EXPIRATION_DELTA': datetime.timedelta(hours=1),
'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=7),
}
My urls.py
path('check-auth', views.check_if_logged_in, name="check-auth"), # check auth
path('auth/obtain_token', obtain_jwt_token), # obtain token
path('auth/refresh_token', refresh_jwt_token),
My views.py
# Login Check
#csrf_exempt
def check_if_logged_in(request):
authentication_class = (JSONWebTokenAuthentication,)
permission_classes = (IsAuthenticated,)
print(request.user) # returns AnonymUser
check = None
if request.user.is_authenticated:
check = True
else:
check = False
print(check) # returns False
return HttpResponse(f"<html><body>{check}</body></html>")
Vue
obtainToken function
obtainToken(){
var that = this;
fetch(this.endpoints.obtainJWT, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
username: that.django.username,
password: that.django.password
})
}).then(response => response.json()
).then(function(response) {
console.log('auth', response); # get token
that.updateToken(response.token); # update localStorage
that.checkAuthData(); #check auth
});
},
checkAuth function
checkAuthData: function() {
var that = this;
fetch('http://localhost:8000/check-auth', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
token: this.jwt # send token
})
}).then(response => response.json()
).then(function(response) {
console.log('check', response);
});
},
You should include token not in the body, but in the header instead:
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + this.jwt
},
Also, please make sure that in your Django settings in REST_FRAMEWORK DEFAULT_AUTHENTICATION_CLASSES contains JWT authentication:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
...
'rest_framework_simplejwt.authentication.JWTAuthentication',
]
}
I'm building a basic app with login, register, etc. now i try finish the login using Django like backend and fronted with Angular, but i can't end my login because this error, when i try login with the correct credentials and redirect to new page or url show this error.
TypeError at /doctor
'list' object is not callable"in network panel"
service .ts
constructor(private http: Http, private httpClient: HttpClient) { }
private headers = new Headers({ 'Content-Type': 'application/json' });
getDoctores(): Promise<Doctor[]> {
return this.http.get(this.baseurl + '/doctor?format=json', { headers: this.headers })
.toPromise()
.then(response => response.json() as Doctor[])
}
component .ts
constructor(private dataService: dataService, public dialog: MatDialog,
private router: Router) {
this.getDoctores();
this.selectedDoctor = {
id: -1, nombreDoc: '', apellidoDoc: '', rutDoc: '', direccionDoc: ''
, telefonoDoc: '', release_date: ''
}
}
getDoctores(): void {
this.dataService
.getDoctores()
.then(doctores => this.doctores = doctores);
}
url.py
path('auth/login/', obtain_jwt_token),
path('auth/refresh-token/', refresh_jwt_token),
url(r'^doctor$', views.DoctorList.as_view()),
url(r'^doctor/(?P<pk>[0-9]+)$', views.DoctorDetail.as_view()),
view.py
class DoctorList(generics.ListCreateAPIView):
queryset = Doctor.objects.all()
serializer_class = DoctorSerializer
class DoctorDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Doctor.objects.all()
serializer_class = DoctorSerializer
A have an Ember (v2.12.0-beta.1) app that uses ember-simple-auth-token to request a JWT.
The important part happens in the login controller.
export default Ember.Controller.extend({
session: Ember.inject.service(),
// Properties
username: 'user1',
password: 'password123',
// Actions
actions: {
login(username, password) {
console.log('Attempting login...');
let creds = this.getProperties('username', 'password');
let authenticator = 'authenticator:jwt';
this.get('session').authenticate(authenticator, creds).then(function() {
console.log('LOGIN SUCCESS')
}, function() {
console.log('LOGIN FAIL')
});
}
}
});
When submitting the form, there is a request that is being made by the browser and my backend receives it.
The problem is that only the password is included in the request. The body of the request has the form {"password":"password123"}, but it should look like {"username":"user1","password":"password123"}. Of course, the login attempt fails and LOGIN FAIL is printed.
Why is the username not included in the token request?
I tried using earlier versions of ember-simple-auth-token and ember-simple-auth.
Here is my configuration:
ENV['ember-simple-auth'] = {
authorizer: 'authorizer:token',
};
ENV['ember-simple-auth-token'] = {
serverTokenEndpoint: 'http://127.0.0.1:6003/token',
identificationField: 'username',
passwordField: 'password',
tokenPropertyName: 'token',
authorizationPrefix: 'Bearer ',
authorizationHeaderName: 'Authorization',
refreshAccessTokens: false,
};
ember-simple-auth-token expects credentials object passed to authenticate to be in format:
{
identification: <username>,
password: <password>
}
So your code should look something like this:
actions: {
login(username, password) {
console.log('Attempting login...');
let creds = {
identification: username,
password: password
};
let authenticator = 'authenticator:jwt';
this.get('session').authenticate(authenticator, creds).then(function() {
console.log('LOGIN SUCCESS')
}, function() {
console.log('LOGIN FAIL')
});
}
}
The request sent in this case is:
{
"password":"password123",
"username":"user1"
}
There are some pull requests about this issue.