i am trying to develop django/angular project and using base user model for django user.
I tried to implement JWT auth in both side. On django side i am using rest_framework_jwt library.
I checked the token flow from client-side to server-side. With a simple login page on angular i use my username and password for logging in. It's accepting my data and redirects me to the main page where i use a service to get "feed" data from server-side. In this step it gives me 403 error. By the way when i use Postman for checking the scenerios im adding jwt as a authorization header like "JWT " and it still give me "Authentication credentials were not provided". Waiting your helps. Thanks.
django -> settings.py middleware ;
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
django -> settings py rest_framework ;
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_AUTHENTICATON_CLASSES':(
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
),
'NON_FIELD_ERRORS_KEY': 'global',
}
i use these settings for jwt
#JWT SETTINGS
JWT_AUTH = {
'JWT_ALLOW_REFRESH': True,
'JWT_EXPIRATION_DELTA': timedelta(days=2) # datetime imported at start
}
using classic urls.py ( its and included part so it begins with api/auth/ )
urlpatterns = [
path('login/', obtain_jwt_token),
path('refresh-token/', refresh_jwt_token),
]
in front end i have a interceptor, authservice and feedservice
authservice
import { Injectable } from '#angular/core';
import { HttpClient, HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '#angular/common/http';
import { tap, shareReplay } from 'rxjs/operators';
import * as JwtDecode from 'jwt-decode';
import * as moment from 'moment';
import { JWTPayload } from '../interfaces/JWTPayload';
// logs in and out, notifies other components with subscription
#Injectable({
providedIn: 'root'
})
export class AuthenticationService {
URL_API = 'http://localhost:80/api/auth';
constructor(private http: HttpClient) { }
private setSession(authResult) {
const token = authResult.token;
const payload = <JWTPayload>JwtDecode(token);
const expiresAt = moment.unix(payload.exp);
localStorage.setItem('token', authResult.token);
localStorage.setItem('expires_at', JSON.stringify(expiresAt.valueOf()));
}
get token(): string {
return localStorage.getItem('token');
}
login(username: string, password: string) {
return this.http.post(`${this.URL_API}/login/`, { username, password })
.pipe(tap(
response => {
console.log("API CALL RESPONSE FOR LOGIN => ", response);
this.setSession(response);
}
),
shareReplay(),
);
}
logout() {
localStorage.removeItem('token');
localStorage.removeItem('expires_at');
console.log("CIKIS YAPILDI ");
}
refreshToken() {
if (moment().isBetween(this.getExpiration().subtract(1, 'days'), this.getExpiration())) {
return this.http.post(
`${this.URL_API}/refresh-token/`,
{ token: this.token }
).pipe(
tap(response => { this.setSession(response); console.log("response for refresh token=>", response); }),
shareReplay(),
).subscribe();
}
}
getExpiration() {
const expiration = localStorage.getItem('expires_at');
const expiresAt = JSON.parse(expiration);
console.log("expires=>", moment(expiresAt).calendar());
return moment(expiresAt);
}
isLoggedIn() {
return moment().isBefore(this.getExpiration());
}
isLoggedOut() {
return !this.isLoggedIn();
}
}
interceptor
import { Injectable } from '#angular/core';
import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '#angular/router';
import { AuthenticationService } from '../services/authentication.service';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '#angular/common/http';
import { Observable } from 'rxjs';
// intercepts every api call and adds jwt header to call.
#Injectable({
providedIn: 'root'
})
export class AuthInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const token = localStorage.getItem('token');
console.log("firs token check => ", token);
if (token) {
console.log("gecerli token=> ", token)
const cloned = req.clone({
headers: req.headers.set('Authorization', 'JWT '.concat(token))
});
console.log("cloned ->", cloned.body);
return next.handle(cloned);
} else {
console.log("next.handle(req) => ", next.handle(req));
return next.handle(req);
}
}
}
feedservice
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { Observable } from "rxjs";
import { Post } from '../interfaces/Post';
#Injectable({
providedIn: 'root'
})
export class FeedService {
API_URL = 'http://localhost:80'
constructor(private httpClient: HttpClient) { }
public getFeed(): Observable<Post[]> {
return this.httpClient.get<Post[]>(`${this.API_URL}/api/feed/`);
}
public retrievePost(post_id: number): Observable<Post> {
return this.httpClient.get<Post>(`${this.API_URL}/api/feed/${post_id}`)
}
public postFeed(post: Post) {
return this.httpClient.post(`${this.API_URL}/api/feed/`, post)
}
}
and in finale there is login component
import { Component, OnInit } from '#angular/core';
import { FormBuilder, FormGroup, Validators } from '#angular/forms';
import { Router, ActivatedRoute } from '#angular/router';
import { first } from 'rxjs/operators';
import { AuthenticationService } from '../services/authentication.service';
#Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
loginForm: FormGroup
returnUrl: string
loading = false;
submitted = false;
error = '';
constructor(
private formBuilder: FormBuilder,
private authenticationService: AuthenticationService,
private router: Router,
private route: ActivatedRoute
) {
//redirect home if already logged in
if (authenticationService.isLoggedIn()) {
console.log("isLoggedIn() => already logged in")
router.navigate(['/']);
}
}
ngOnInit(): void {
this.loginForm = this.formBuilder.group({
username: ['', Validators.required],
password: ['', Validators.required]
});
//get returl url or redirect to /
this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
}
//easy way to access fields
get f() { return this.loginForm.controls }
onSubmit() {
this.submitted = true;
// stop here if form is invalid
if (this.loginForm.invalid) { console.log("if code block => invalid credentials"); return; }
this.loading = true;
this.authenticationService.login(this.f.username.value, this.f.password.value)
.subscribe(
success => { console.log("succesfull login"); this.router.navigate([this.returnUrl]); },
error => {
this.error = error;
this.loading = false;
console.log("errors =>", error);
}
);
}
}
I figured it out. In my other api's view i added those lines
permission_classes = [ IsAuthenticated ]
authentication_classes = [ JSONWebTokenAuthentication ]
and it worked!
Related
I need to build an application where only logged in users can access a type of information using Angular in Front and Django Rest Framework in back.
I just can't do this authentication, I tried and researched in every way, even using simple-jwt, I can't succeed.
Could you please help me or suggest me a site with a complete example.
Here is my code below:
settings.py
# ALLOWED_HOSTS=['*']
# CORS_ORIGIN_ALLOW_ALL = True
ALLOWED_HOSTS=['localhost', '127.0.0.1']
CORS_ORIGIN_ALLOW_ALL = False
CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_WHITELIST = (
'http://localhost:4200',
)
# JWT settings
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
'AUTH_HEADER_TYPES': ('Bearer',),
}
accounts/views.py
class UserViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
queryset = MyUser.objects.all()
serializer_class = UserSerializer
# renderer_classes = (UserJSONRenderer,)
permission_classes = [permissions.IsAuthenticated]
account/urls.py
# app_name = 'accounts'
router = routers.DefaultRouter()
router.register(r'users', views.UserViewSet)
router.register(r'groups', views.GroupViewSet)
# Wire up our API using automatic URL routing.
# Additionally, we include login URLs for the browsable API.
urlpatterns = [
path('', include(router.urls)),
path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
]
And from my Angular
auth.service.ts
import { HttpClient, HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpHeaders } from '#angular/common/http';
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs';
import { tap, shareReplay } from 'rxjs/operators';
import * as jwtDecode from 'jwt-decode';
import * as moment from 'moment';
const AUTH_API = "http://localhost:8000/users/";
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
#Injectable({
providedIn: 'root'
})
export class AuthService {
constructor(private http: HttpClient) { }
login(username: string, password: string): Observable<any> {
return this.http.post(
AUTH_API + 'login/',
{
username,
password,
},
httpOptions,
);
}
register(username: string, email: string, password: string): Observable<any> {
return this.http.post(
AUTH_API + 'signup',
{
username,
email,
password,
},
httpOptions
);
}
logout(): Observable<any> {
return this.http.post(AUTH_API + 'signout', { }, httpOptions);
}
}
http.itercepto.ts
import { Injectable } from '#angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HTTP_INTERCEPTORS } from '#angular/common/http';
import { Observable } from 'rxjs';
#Injectable()
export class HttpRequestInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
req = req.clone({
withCredentials: true,
});
return next.handle(req);
}
}
export const httpInterceptorProviders = [
{ provide: HTTP_INTERCEPTORS, useClass: HttpRequestInterceptor, multi: true },
];
Currently, after logging in I'm able to get the JWT to the frontend. My app currently has a logging page as the landing page and as soon as the user logins the route checks for authentication to redirect to the guarded home path.
My first intuition was to send a boolean from the backend (Django) and use that to create a guard. But I keep seeing that seems to be better practice to handle this in the front end.
What I did was create an auth.service.ts and an auth.guard.ts. In the service, I try to retrieve the token from the browser and then verify that it hasn't expired. Then I call that method on the guard and return a boolean. Problem is that every time I look for the token in the local storage, I get back null.
Is there any better way to get achieve this?
auth.guard.ts
import { Injectable } from '#angular/core';
import {
ActivatedRouteSnapshot,
CanActivate,
Router,
RouterStateSnapshot,
UrlTree,
} from '#angular/router';
import { Observable, of } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import { AuthService } from './auth.service';
#Injectable({
providedIn: 'root',
})
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) {}
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
):
| Observable<boolean | UrlTree>
| Promise<boolean | UrlTree>
| boolean
| UrlTree {
console.log(this.authService.isAuthenticated());
if(!this.authService.isAuthenticated()){
this.router.navigate(['/login']);
return false;
}
return true;
}
}
auth.service.ts
import { Injectable } from '#angular/core';
import { JwtHelperService } from '#auth0/angular-jwt';
#Injectable({
providedIn: 'root'
})
export class AuthService {
public jwtHelper: JwtHelperService = new JwtHelperService();
constructor() { }
isAuthenticated(){
const jwt = localStorage.getItem('token');
return !this.jwtHelper.isTokenExpired(jwt!);
}
}
app-routing.module.ts
...
import { AuthGuard } from './user/services/auth.guard'
const routes: Routes = [
{
path: '',
component: LandingComponent,
children: [
{ path: '', component: HomeComponent, canActivate: [AuthGuard],},
{ path: 'home', component: HomeComponent, canActivate: [AuthGuard],},
{
path: 'cohort-charts',
component: CohortChartsComponent,
children: [
{ path: 'selection', component: CohortSelectionComponent },
{ path: 'edit', component: CohortEditComponent },
{ path: '', redirectTo: 'selection', pathMatch: 'full' },
],
},
],
},
{
path: 'login',
component: LoginComponent,
},
{ path: '**', redirectTo: '' },
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}
I guess that every time you look for the token in the local storage, you get back null because you aren't saving the token, or if you do, you are trying to store the token as object, not serialized (as string, stringifying it) , so it doesn't store, or when you get it, you aren't pasing it.
Any way, I guess that the best practice to manage the whole jwt/authentification section, would be with an interceptor:
And Interceptor is a service which intercepts all your http calls, and you cans set that it does something authomatically (for instance, magaging the jwt).
More info about how to Adding and updating headers and how to Use the interceptor for Intercepting requests and responses:
https://angular.io/guide/http#adding-and-updating-headers
https://angular.io/guide/http#intercepting-requests-and-responses
I leave you a glimpse of how you do it /what you need:
Provide the Angular Interceptor
app.module.ts:
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: HttpJwtAuthInterceptor,
multi: true,
},
{ provide: BASE_PATH, useValue: environment.apiUrl },
],
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 ${myToken}`,
'Content-Type': 'application/json',
},
url,
});
}
…
return next.handle(request);
}
I have created a Django and Angular application to upload files. It was working without errors until I integrated a login page. I have not been able to upload files since integration. I get 401 - "Unauthorized" error. What could have possibly gone wrong?
Auth-interceptor:
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest,HttpErrorResponse } from "#angular/common/http";
import { Injectable } from "#angular/core";
import { catchError, Observable, throwError } from "rxjs";
import { LoginService } from "src/services/login.service";
#Injectable()
export class AuthInterceptorService implements HttpInterceptor {
constructor(private authService: LoginService) {}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (this.authService.isLoggedIn()) {
const token = this.authService.getAuthToken();
console.log("intercept",token)
// If we have a token, we set it to the header
request = request.clone({
setHeaders: {Authorization: `Token ${token}`}
});
}
return next.handle(request)
}
}
fileupload.component.ts:
import { Component, OnInit } from '#angular/core';
import { FormBuilder, FormGroup } from '#angular/forms';
import { LoginService } from 'src/services/login.service';
import { FileUploader, FileLikeObject } from 'ng2-file-upload';
import { concat, Observable } from 'rxjs';
import { HttpEvent, HttpEventType } from '#angular/common/http';
#Component({
selector: 'app-fileupload',
templateUrl: './fileupload.component.html',
styleUrls: ['./fileupload.component.scss']
})
export class FileuploadComponent {
DJANGO_SERVER = 'http://127.0.0.1:8081'
public uploader: FileUploader = new FileUploader({});
public hasBaseDropZoneOver: boolean = false;
constructor(private formBuilder: FormBuilder, private uploadService: LoginService) { }
fileOverBase(event): void {
this.hasBaseDropZoneOver = event;
}
getFiles(): FileLikeObject[] {
return this.uploader.queue.map((fileItem) => {
return fileItem.file;
});
}
upload() {
let files = this.getFiles();
console.log(files);
let requests= [];
files.forEach((file) => {
let formData = new FormData();
formData.append('file' , file.rawFile, file.name);
requests.push(this.uploadService.upload(formData));
console.log(requests,file)
});
concat(...requests).subscribe(
(res) => {
console.log(res);
},
}
);
}}
console.log(err);
}
);
}}
service:
public upload(formData) {
let token= localStorage.getItem('token');
return this.http.post<any>(`${this.DJANGO_SERVER}/upload/`, formData).pipe(map((res) => {
console.log(res)
})
)
}
Thank you
I resolved the issue. It was because I was usign interceptor and I was using third party API for authentication. So instead of Django token, the third party APIs token was sent in header of POST request.
How I resolved it?
I used Httpbackend to process POST requests to Django DB so that the request is not intercepted and then I added custom header (with Django token to the reuest). I used the code snippet on this website: https://levelup.gitconnected.com/the-correct-way-to-make-api-requests-in-an-angular-application-22a079fe8413
I'm new to Angular and I'm creating a test Application to accelerate my understanding of the topic. Recently I encountered a challenge to integrate Angular2(FrontEnd) with Django(Backend) by fetching the data using REST APIs.
File: library.service.ts
import 'rxjs/add/operator/map';
import { Observable } from 'rxjs/Rx';
import { Injectable } from '#angular/core';
import { Headers, Http, Response } from '#angular/http';
// Project Modules
import { Library } from '../models';
#Injectable()
export class LibraryService {
private librariesUrl = 'http://127.0.0.1:8000/api/library/create-library/';
constructor(private http: Http) { }
private headers = new Headers({'Content-Type': 'application/json'});
private extractData(res: Response) {
return res.json();
}
private handleError (error: any) {
return Observable.throw(error.message || 'Server error');
}
getAll(): Observable<Library[]> {
return this.http.get(this.librariesUrl, {headers: this.headers})
.map((res) => this.extractData(res.json())).catch((err) => this.handleError(err));
}
}
File: libraries.component.ts
import { Component, OnInit} from '#angular/core';
import {HttpClient} from '#angular/common/http';
// Project Modules
import { Library } from '../models';
import { LibraryService } from './library.service';
#Component({
selector: 'app-libraries',
templateUrl: './libraries.component.html',
styleUrls: ['./libraries.component.css'],
})
export class LibrariesComponent implements OnInit {
libraries: Library[];
personalLibraries: Library[];
collaborativeLibraries: Library[];
constructor(private libraryService: LibraryService, private http: HttpClient) { }
ngOnInit(): void {
/*this.http.get('http://127.0.0.1:8000/api/library/create-library/').subscribe((data: Library[]) => {
console.log(data);
this.personalLibraries = data;
});*/
this.libraryService.getAll().subscribe(response => this.personalLibraries = response);
}
}
REST API
# Django Modules
from django.shortcuts import get_object_or_404
# REST Modules
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.decorators import api_view, authentication_classes, permission_classes
# Project Modules
from .models import Resource, ResourceUserAssociation, Collection, Library
from mysite.utils import get_value_or_404, get_value_or_default, get_boolean
from .serializers import LibrarySerializer, CollectionSerializer
# TODO: Check user authentication here
class CreatorLibraryAPI(APIView):
def get(self, request, format=None):
# slug = get_value_or_404(request.GET, 'slug')
lib_object = Library.objects.filter(type='personal')
sdata = LibrarySerializer(lib_object, many=True).data
return Response(sdata, status=status.HTTP_200_OK)
JSON I'm Expecting
[
{
"slug": "tech-stack",
"title": "Technology Stack",
"description": "Library of resources related to Technology",
"type": "personal"
},
{
"slug": "biz-stack",
"title": "Technology Stack",
"description": "Library of resources related to Business",
"type": "personal"
},
{
"slug": "design-stack",
"title": "Design Stack",
"description": "Library of resources related to Design",
"type": "personal"
}
]
Important
When I try to fetch data in the Component only, then I successfully get the result [See the commented code in libraries.components.ts]. But somehow it's not working in the Service, am I doing something wrong with Observables?
Note
This problem is very similar to Question here.
Big thanks to the community in advance :)
Few changes I've made:
Used HttpClient instead of Http. This allows me to remove .map() as HttpClien already returns the JSON (instead of the whole response).
Correct File: library.service.ts
import 'rxjs/add/operator/map';
import { Observable } from 'rxjs/Rx';
import { Injectable } from '#angular/core';
import { Headers } from '#angular/http';
import {HttpClient} from '#angular/common/http';
// Project Modules
import { Library } from '../models';
#Injectable()
export class LibraryService {
private librariesUrl = 'http://127.0.0.1:8000/api/library/create-library/';
constructor(private http: HttpClient) { }
private headers = new Headers({'Content-Type': 'application/json'});
private handleError (error: any) {
console.log('---------- CATCH ERROR ----------');
return Observable.throw(error.message || 'this is some random server error');
}
getAll(): Observable<Library[]> {
return this.http.get(this.librariesUrl).catch((err) => this.handleError(err));
}
}
I am new to the ionic framework,I had written the post call, I don't know exactly the post method.When i entered the submit button in forgot password page,if the user is already registered then should display the next page else it should display the alert message.
Below is my code:
import { Component } from '#angular/core';
import { configurator } from '../../providers/configurator';
import { NavController } from 'ionic-angular';
// import { LoginPage } from '../login/login';
import { persistence } from '../persistence/persistence';
#Component({
templateUrl: 'home.html'
})
export class home {
public loginId = "";
constructor(public navCtrl: NavController) {
}
generateOTP(newstate) {
console.log("invoking generateOTP FN");
var _this = this;
this.login.generateOTP(this.loginId, function(result,data){
if(result == '1') {
alert(data);
var a = document.createElement('a');
a.href="OTP page";
}
else {
//this.showRePasswd = this.showRePasswd;
alert(data);
}
})
}
}
This is my ionic-page:enter image description here
Can anyone help me!!!
my post belowed in IONIC2
import { Http, Response } from '#angular/http';
constructor(public navCtrl: NavController, private http: Http) { }
this.http.post(`${POST_URL}/log/add`, JSON.stringify(this.currentLocation), {headers})
.toPromise()
.then((response) => {
if (response.status !== 201) {
this.trace.error('log','postLog',`error response: ${response.status}`);
}
})
.catch(error => {
this.trace.error('log','postLog',`err post log:${error}`);
});