Trying to make Login feature using angular-2 where I have used one login component, authservice, logingard with canActive.
While following example from angular2 document, I don't get value of loggedin in guard file and it asks every time for login
Posting sample code here
AuthService
export class AuthService {
loggedIn: boolean = false;
isLoggedIn() {
return this.loggedIn;
}
login(credentials) {
let user = JSON.stringify(credentials);
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this._http.post(this._apiUrl, user, options)
.map(res => res.json())
.map((res) => {
this.loggedIn = true;
}
return res;
});
}}
Loggedin Guard
export class LoggedInGuard implements CanActivate {
constructor(private _auth: AuthService, private _router: Router) {}
canActivate() {
//Get problem here. Everytime this comes false
if(this._auth.isLoggedIn()) { return true; }
this._router.navigate(['login']);
}
}
login component
export class LoginComponent {
constructor(private userService: AuthService, private router: Router) {}
onSubmit(email, password) {
this.userService.login(email, password).subscribe((result) => {
if (result) {
this.router.navigate(['']);
}
});
}
}
Btw, following example of
https://medium.com/#blacksonic86/angular-2-authentication-revisited-611bf7373bf9#.723a4ssji
without localstorage
Related
I am using NestJS and its JWT package based on jsonwebtoken. The generated token is always being invalid, and I am getting a 500 - Internal Server Error.
What might be the problem?
My login function in the AuthService:
async login(email: string, password: string, isAdmin?: boolean, isVIP?: boolean){
let user = await this.usersService.findByEmail(email);
if(!user){
throw new NotFoundException('No user with this email could be found.');
}
const isEqual = await bcrypt.compare(password, user.password);
if(!isEqual){
throw new BadRequestException('Email and password do not match');
}
const secret = 'secretkey';
const payload = {email: user.email, userId: user._id.toString()}
const token = this.jwtService.sign(payload, {secret, expiresIn: '1h'});
return [email, isAdmin, isVIP, token];
}
My verification logic in the AuthGuard
`
import { BadRequestException, CanActivate, ExecutionContext, Inject } from "#nestjs/common";
import { JwtService } from "#nestjs/jwt/dist";
import { JwtConfigService } from "src/config/jwtconfig.service";
export class JwtAuthGuard implements CanActivate {
constructor(#Inject(JwtService) private jwtService: JwtService){}
canActivate(context: ExecutionContext) {
const request = context.switchToHttp().getRequest();
const authHeader = request.get('Authorization');
if (!authHeader) {
throw new BadRequestException('Not authorized');
}
const token = authHeader.split(' ')[1];
let decodedToken;
try {
decodedToken = this.jwtService.verify(token, {secret: 'secretkey'});
} catch (err) {
throw new Error('Cannot verify token.')
}
if(!decodedToken){
throw new BadRequestException('Not authenticated')
}
request.userId = decodedToken.userId;
console.log({decodedToken, token});
return request.userId;
};
}
My current JWT setup in the UsersModule imports (I have no AuthModule); I tried all the other configurations in the official docs, too.
JwtModule.register({
secret: 'secretkey',
publicKey: '...',
privateKey: '...',
secretOrKeyProvider: (
requestType: JwtSecretRequestType,
tokenOrPayload: string | Object | Buffer,
verifyOrSignOrOptions?: jwt.VerifyOptions | jwt.SignOptions
) => {
switch (requestType) {
case JwtSecretRequestType.SIGN:
return 'privateKey';
case JwtSecretRequestType.VERIFY:
return 'publicKey';
default:
return 'secretkey';
}
},
})
`
My jwtconfig.ts, which I don't think is being used:
`
import { JwtOptionsFactory, JwtModuleOptions } from '#nestjs/jwt'
export class JwtConfigService implements JwtOptionsFactory {
createJwtOptions(): JwtModuleOptions {
return {
secret: 'secretkey'
};
}
}
`
I solved the problem by switching my guard to a middleware.
Here is my code
handleLogin = async()=> {
const { navigation } = this.props;
const errors = [];
Keyboard.dismiss();
this.setState({ loading: true });
if(this.state.email!=''){
if(this.state.password!=''){
fetch('http://192.168.1.10:8000/api/login',{
method:'post',
headers:{
'Content-Type':'application/json',
'Accept': 'application/json'
},
body:JSON.stringify({
"email":this.state.email,
"password":this.state.password
})
})
.then((response)=> response.json())
.then((res)=>{
var store=JSON.stringify(res)
this.setState({ errors, loading: false });
if(this.state.store==='{"error":"Unauthorised"}'){
Alert.alert("Error", "These credentials do not match our records");
}
else {
Alert.alert("Success","You have succesfuly login",
[
{
text: 'Continue', onPress: () => {
Actions.NavigationCalling();
}
}
],
{ cancelable: false })
}
}).catch((error)=>{
console.error(error);
});
}
else{
this.setState({ errors, loading: false });
Alert.alert("Please insert Password")
}
}
else{
this.setState({ errors, loading: false });
Alert.alert("Please insert email")
}
var store=JSON.stringify(res)
Im storing data in store variable that print output like this
{"success":{"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6Ijk4YjYzNTY4NjNkNmYxNzBhYjdlYjVmNWRiN2MxNWU1NTUzNDc3OWUyZjM5Mjc5NWU4ODMzMjM3NDU1Y2Q3ODg1YzYzZTc0NDg2MmNjYzk2In0.eyJhdWQiOiIxIiwianRpIjoiOThiNjM1Njg2M2Q2ZjE3MGFiN2ViNWY1ZGI3YzE1ZTU1NTM0Nzc5ZTJmMzkyNzk1ZTg4MzMyMzc0NTVjZDc4ODVjNjNlNzQ0ODYyY2NjOTYiLCJpYXQiOjE1ODA5MzA2MzYsIm5iZiI6MTU4MDkzMDYzNiwiZXhwIjoxNjEyNTUzMDM2LCJzdWIiOiIzIiwic2NvcGVzIjpbXX0.A1NZcxICOu8mudwrIrozG5FYiQxWyz1O8LcHFcaOvgXUlTkgUIuGuwxif74goMwCLkWrm3wIwZSKwMAxCAk35Ao9VZEsV3uOlelWtsjJY7u-o00baCUl3dZWJeBHLLfSODM719Oinrfepp5VGaGZ4r--rMqMnNljVEoUP8GuM0l_7rY-SA7dhXkj4a8TwogkZOzf1_0ZvgNYmM30Z_CU0umM72Iqcys-URnzb80HONI4_cVcYExqmU94UqhNsNJ9aMIDXR4WdMGzDBzhRat_E75u7Rbt67UKUbbwALv3J1qhGRb-kkE_DGR3DyAxlcNvMy21CR4b4obDE4e96GYb-7R7fLw0PiVtiyFTgLeL2Ldvw4YV8_v2TwF5zLgkh0VCqfjUTIbAir9ytjDBPDzXFy7G4mAR6qJYNPSHtgwzBcMuS2B4FWZruWg-0QbHiBFFQrDGISaf5jUTSCmiSbpd3eTKAhiifBE6eGNnzErlsG1WAn8L7zK233l1b7qDSoahIrK4PTllgsdEzFtI4sXpy_9mEbRmGmEjOTXZcUyvkFm6aajXrRwWGrhSMFIeFqRd2BNTnrh2Igwl9x7Dcj89VgXKGXyh-hP4ESwT5yHAynNhVfhaXAu1kdnkzhwzk5cDlKlYawvX02g83THN6UAElj2bttIutM-fZ1ViLIOA98k"}}
But i want to store just token in new state and pass to other screen
how can i get only token from json object and store into newstate and pass into new screen
Thanks in advance I'm new in the React native
You can access the token by doing res.success.token or you can map the json response to an object.
.then((res)=>{
var token = res.success.token // did you try doing this?
var store=JSON.stringify(res)
this.setState({ errors, loading: false });
....
And to pass the token to another screen you can use the screen props or use redux, however, i would set it in a static variable to be accessed globally by all the screens.
I am working on a react native app that does pretty much the same that you are trying to do, i created a class called ApiService that has the following function:
static async post(url: string, payload?: object, headers?: HeadersInit_): Promise<ApiResponse> {
return await fetch(url, {
method: "POST",
headers: headers,
body: JSON.stringify(payload),
}).then(r => r.json().then(o => {
var mapper = new ApiResponse();
mapper.success = o.success;
mapper.result = o.success ? JSON.stringify(o.result) : o.error;
return mapper;
})).then(response => {
if (response.success)
return response;
throw ApiError(response);
});
}
The ApiResponse class looks like this:
class ApiResponse {
public success: boolean;
public result: string;
}
To access the auth token globally, i am setting the token in a static variable in a class that can be accessed everywhere.
class AbstractService {
private static sessionToken: string;
public static setSessionToken(value: string): void {
AbstractService.sessionToken = value;
}
public static getSessionToken(value: string): string {
return AbstractService.sessionToken;
}
If you want to store the token to be reused, you can use react-native-secure-key-store
I have url, this url needs authentication with username and password. On PHP I made it with curl_setopt(CURLOPT_USERPWD, $username . ':' . $password), I can't find any solution how to set password and username on same link on NestJS.
import { Injectable, Scope, HttpService } from '#nestjs/common';
import { ImporterService } from '../importer.service';
import { map } from 'rxjs/operators';
#Injectable({ scope: Scope.REQUEST })
export class Supplier extends ImporterService {
supplierName = 'supplierName';
fileDownload: boolean = false;
username = 'username;
password = 'password';
async parse() {
await this.checkSupplierExist(this.supplierName);
return this.httpService
.get(
'https://www.link.com/restful/export/api/products.xml?acceptedlocales=en_US&addtags=true',
)
.pipe( map(response => console.log(response)));
}
}
this response sends 401, cause it needs authentication. I cannot figure out how to set username or password. Can anyone help?
From me I have a Config service which handles http calls, looks like this:
#Injectable()
export class ConfigService {
private readonly envConfig: { [key: string]: string };
private servers: Object = {};
constructor(private httpService: HttpService) {
this.envConfig = {
PORT: process.env.PORT,
MONGODB_URI: process.env.MONGODB_URI,
MONGODB_SESSION_URI: process.env.MONGODB_SESSION_URI,
SESSION_SECRET: process.env.SESSION_SECRET
};
const externals = JSON.parse(process.env.EXTERNALS);
for (const lang in externals) {
this.servers[lang] = externals[lang];
}
}
get(key: string): string {
return this.envConfig[key];
}
getServer(lang: string, method: string, url: string, headers: Object, params?: Object, data?: Object) {
const config = { baseURL: this.servers[lang], url, headers, method, data, params };
return this.httpService.request(config).toPromise();
}
}
As you can see in the getServer method, you can add headers as well.
I hope this is what you're looking for
I have a piece of code (auth.js) that is supposed to be triggered after a user successfully logs in. But I noticed that 2 lines do not seem working. it is not tiggered.
this.$axios.defaults.headers.common['Authorization'] = 'Bearer ' + token;
and
this.$eventHub.$emit('userLoggedIn');
is there something i need to do to make both work? i have set them up to be accessible globally. i checked out the line:
localStorage.setItem('user', JSON.stringify(user));
and seems to work fine.
i have tried remove this word but to no avail.
main.js
import auth from '#/auth'
import axios from 'axios'
Vue.prototype.$axios = axios
Vue.prototype.$auth = auth
Vue.prototype.$eventHub = new Vue();
auth.js
class Auth {
constructor() {
this.token = null;
this.user = null;
}
login(token, user) {
localStorage.setItem('token', token);
localStorage.setItem('user', JSON.stringify(user));
this.$axios.defaults.headers.common['Authorization'] = 'Bearer ' + token;
this.token = token;
this.user = user;
this.$eventHub.$emit('userLoggedIn');
}
check() {
return !! this.token;
}
}
export default new Auth();
this is the code inside my Login.vue which calls the auth.js login method:
login() {
let data = {
email: this.username,
password: this.password
};
this.$axios.post('http://127.0.0.1:8000/api/login', data)
.then(({data}) => {
if (data.status == 200) {
this.$auth.login(data.data.token, data.data.user);
this.$router.replace('about');
} else {
// console.log("Error");
}
})
.catch(() => {
//alert(response.data.message);
});
}
The this keyword in the Auth class is not pointing to the Vue instance. You can pass the Vue instance as a parameter of login method like this:
class Auth {
constructor() {
this.token = null;
this.user = null;
}
login(vue, token, user) {
localStorage.setItem('token', token);
localStorage.setItem('user', JSON.stringify(user));
vue.$axios.defaults.headers.common['Authorization'] = 'Bearer ' + token;
this.token = token;
this.user = user;
vue.$eventHub.$emit('userLoggedIn');
}
check() {
return !! this.token;
}
}
export default new Auth();
And then call login method this way:
this.$auth.login(this, data.data.token, data.data.user);
My login function consists of a http request (an irrelevant one just for the check) with the entered credentials. This way I can resolve the request or get a rejection which I handle by not pushing the next component with the NavController onto the stack.
On logout, the credentials, which are saved in the Ionic Storage, are deleted. Now starts the problem: Any credentials that are saved now in the storage seems not to be used by the login request as the request doesn't throw an Auth exception. Only after clearing the Browser cache it works again.
This all happened by serving the app in a web browser and on the phone.
How is it possible to clear the App cache (not only the View/Component cache) with Ionic 2 to prevent this behaviour? There is no documentation or question concerning this problem existent at the moment.
Auth Service:
#Injectable()
export class AuthService {
HAS_LOGGED_IN = 'hasLoggedIn';
constructor(private storage: Storage) {
// this.rest = rest;
console.log('auth');
}
setCredentials(credentials) {
this.storage.set('username', credentials.username);
this.storage.set('password', credentials.password);
}
logout(): void {
this.storage.remove('username');
this.storage.remove('password');
this.storage.remove(this.HAS_LOGGED_IN);
}
hasLoggedIn() {
return this.storage.get(this.HAS_LOGGED_IN).then( value => {
console.log('hasLoggedIN value: ' + value);
return value === true;
});
}
}
Login Component:
#Component({
selector: 'page-login',
templateUrl: 'login.html',
})
export class LoginPage {
model: any;
HAS_LOGGED_IN: string = 'hasLoggedIn';
constructor(private navCtrl: NavController,
private viewCtrl: ViewController,
private auth: AuthService,
private toastCtrl: ToastController,
private rest: RestService,
private storage: Storage) {
}
ionViewDidLoad() {
this.model = {};
}
ionViewWillEnter() {
this.viewCtrl.showBackButton(false);
this.displayTab(false);
}
login() {
console.log(this.model);
console.log('login() claled');
this.displayTab(true);
this.auth.setCredentials(this.model);
this.rest.getEntryPoint().then(data => {
console.log(data);
this.storage.set(this.HAS_LOGGED_IN, true);
this.navCtrl.push(OverviewPage);
}).catch(err => {
this.storage.set(this.HAS_LOGGED_IN, false);
console.log('Error:');
console.log(err);
this.navCtrl.push(LoginPage).then(response => {
console.log(response);
console.log(this.navCtrl);
console.log('pushed login 1');
});
});
}
validate(items: boolean) {
if (items) {
let toast = this.toastCtrl.create({
message: 'Passwort und Benutzername sind zwingend',
duration: 3000,
position: 'bottom',
});
toast.onDidDismiss(() => {
console.log('Dismissed toast');
});
toast.present();
}
}
private displayTab(display: boolean) {
let elements = document.querySelectorAll('.tabbar');
if (elements != null) {
Object.keys(elements).map((key) => {
elements[key].style.transform = display ? 'translateY(0)' : 'translateY(70px)';
});
}
}
}
Intercepting Http methods (this service is used in another service that makes the actual Rest calls):
#Injectable()
export class HttpInterceptorService {
constructor(#Inject(Http) private http: Http, private storage: Storage) {
this.http = http;
console.log('interceptor');
}
get(url) {
return new Promise((resolve, reject) => {
let headers = new Headers();
this.createAuthorizationHeader(headers).then(() => {
return this.http.get(url, {
headers: headers,
}).subscribe(data => {
resolve(data.json());
}, err => {
reject(err);
});
});
});
}
put(url: string, attributes?) {
return new Promise((resolve, reject) => {
let headers = new Headers();
this.createAuthorizationHeader(headers).then(() => {
return this.http.put(url, (attributes) ? attributes : {}, {
headers: headers,
}).subscribe(data => {
resolve(data.json());
}, err => {
reject(err);
});
});
});
}
post(url: string, data) {
return new Promise((resolve, reject) => {
let headers = new Headers();
this.createAuthorizationHeader(headers).then(() => {
return this.http.post(url, data, {
headers: headers,
}).subscribe(output => {
resolve(output.json());
}, err => {
reject(err);
});
});
});
}
private createAuthorizationHeader(headers: Headers): Promise<void> {
console.log('creating auth header');
return new Promise(resolve => {
this.storage.get('username')
.then( username => {
this.storage.get('password')
.then( password => {
headers.append('Authorization', 'Basic ' +
btoa(username + ':' + password));
resolve();
});
});
});
}
}
The other components are using hasLoggedIn() function to check if we are logged in or not when switching back to the App. If not logged in anymore (storage cleaned in any way) we get back to the LoginPage component.
The problem was a session header in the response that I somehow didn't see beforehand.
My solution is a simple Cookies clearing:
window.cookies.clear(function() {});