I am new in angular and firebase and trying to get users data to table only after succesful auth - if you log in correctly, the table shows the data, and if not, you can't see this data. I've tried to make simple firebase login and logout in my AuthService:
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { AngularFireAuth } from '#angular/fire/auth';
import { Router } from '#angular/router';
import * as firebase from 'firebase/app';
#Injectable({
providedIn: 'root',
})
export class AuthService {
constructor(private router: Router, private fireAuth: AngularFireAuth) {}
onLogin(email: string, password: string) {
firebase
.auth()
.signInWithEmailAndPassword(email, password)
.then(function () {
console.log('Succes');
})
.catch(function (error) {
console.log(error);
});
}
async onLogout() {
try {
await firebase.auth().signOut();
this.router.navigate(['./']);
} catch (error) {
console.log(error);
}
}
}
And this is my UserService responsible for displaying users in the table:
import { Injectable } from '#angular/core';
import { Subject } from 'rxjs';
import { take } from 'rxjs/operators';
import { User } from 'src/app/models/user.model';
import { HttpClient } from '#angular/common/http';
#Injectable({
providedIn: 'root',
})
export class UserService {
constructor(private http: HttpClient) {}
fetchUsers() {
let result = new Subject<User[]>();
this.http
.get('https://fir-login-1416c.firebaseio.com/users.json')
.subscribe((users) => {
let usersAr = Object.keys(users).map((id) => new User(users[id]));
result.next(usersAr);
});
return result.pipe(take(1));
}
addUser(user: User) {
let postData: User = user;
this.http
.post<{ name: string }>(
'https://fir-login-1416c.firebaseio.com/users.json',
postData,
{
observe: 'response',
}
)
.subscribe(
(responseData) => {
console.log(responseData.body.name);
},
(error) => {
console.log(error);
}
);
this.fetchUsers();
}
deleteUser() {
// Later
}
}
My firebase database rules looks like this:
{
"rules": {
".write": "auth !== null",
".read": "auth !== null"
}
}
But probelm is when I log in with the correct data and navigate to page with users table I see empty table and console shows
this errors.
It looks like the authentication didn't work at all or I just did something wrong.
If you have any suggestions on how to do this, give them to me :) Thanks!
You need to send some kind of authorisation headers with your request. Otherwise the database doesn't really know that your are authenticated.
I would suggest to use AngularFire not only for authentication but also for fetching data.
Related
I would like anonymous users to be able to only read and write their own data. I have the below as my security rules, but am getting a cannot read error in the simulator and the app.
I'm not sure that I'm going about it the right way. My main objective is to nest new assessments of the same user under their uid's and make it so they can only read, write and update their own assessments.
{
"rules": {
"users": {
"$uid": {
".write": "$uid === auth.uid";
".read": "$uid === auth.uid";
}
}
}
}
This is what my branch currently looks like
This is what I think it should look like to accomplish what I need.
Ideal Database structure
auth.gaurd.ts
import { Injectable } from '#angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '#angular/router';
import { AuthService } from "../../shared/service/auth.service";
import { Observable } from 'rxjs';
import * as firebase from 'firebase';
#Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {
uid: string;
constructor(
public authService: AuthService,
public router: Router
){ }
canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
this.authStateListener();
return true;
}
authStateListener() {
// [START auth_state_listener]
firebase.auth().onAuthStateChanged((user) => {
if (user) {
// User is signed in, see docs for a list of available properties
this.uid = user.uid;
console.log("user"+user.isAnonymous)
console.log("uid"+this.uid)
} else {
// User is signed out
return firebase.auth().signOut().then(() => {
localStorage.removeItem('user');
this.router.navigate(['sign-in']);
})
}
});
}
}
auth.service.ts
import { Injectable, NgZone, ViewChild, ElementRef, Component } from '#angular/core';
import { User } from "../service/user";
import { auth } from 'firebase/app';
import { AngularFireAuth } from "#angular/fire/auth";
import { AngularFirestore, AngularFirestoreDocument } from '#angular/fire/firestore';
import { ActivatedRoute, Router } from "#angular/router";
import * as firebase from 'firebase';
import "firebase/auth";
#Injectable({
providedIn: 'root'
})
export class AuthService {
userData: any; // Save logged in user data
#ViewChild('btnLogin') btnLogin: HTMLButtonElement;
constructor(
public afs: AngularFirestore, // Inject Firestore service
public afAuth: AngularFireAuth, // Inject Firebase auth service
public router: Router,
private actRoute: ActivatedRoute,
public ngZone: NgZone // NgZone service to remove outside scope warning
) {
}
anonymousSignIn(){
firebase.auth().signInAnonymously()
.then(()=>{
this.router.navigate(['assessment']);
console.log("clicking")
}).catch((error) => {
var errorCode = error.code;
var errorMessage = error.message;
console.log("error here")
});
}
**This is the code to push, read, update and delete branches in Firebase. The ReadAssessment list should display all data that the anonymous user owns in order for them to read it. ** fire-assessment.service.ts
import { AuthGuard } from './../shared/guard/auth.guard';
import { Injectable } from '#angular/core';
import {AngularFireDatabase, AngularFireList, AngularFireObject} from '#angular/fire/database';
import * as firebase from 'firebase';
import {Business} from '../models/business';
import { ActivatedRoute, Router } from '#angular/router';
import { map } from 'rxjs/internal/operators/map';
import { isNgTemplate } from '#angular/compiler';
#Injectable({
providedIn: 'root'
})
export class FireAssessmentService {
assessmentsRef: AngularFireList<any>; // Reference to Assessment data list, its an Observable
assessmentRef: AngularFireObject<any>; // Reference to assessment object
public database = firebase.database();
public UserAssessmentInput;
public ref;
public actRoute: ActivatedRoute;
public router: Router;
public auth: AuthGuard;
constructor(private db: AngularFireDatabase) {
}
CreateAssessment(business: Business ){
const key = this.database.ref('/users').push().key;
this.database.ref('/users').child(key).set(
///this.assessmentsRef.ref('/users').push(
{
busiName: business.busiName
});
}
ReadAssessment(id: string){
this.assessmentRef = this.db.object('users/' + id);
return this.assessmentRef;
}
ReadAssessmentsList(){
this.assessmentsRef = this.db.list('users/');
return this.assessmentsRef;
}
UpdateAssessments (business: Business){
this.assessmentRef.update({
busiName: business.busiName
});
}
DeleteAssessment(){
this.assessmentRef = this.db.object('users/');
this.assessmentRef.remove();
}
business.ts
export interface Business {
$key: string;
busiName: string;
}
Right now you're creating data with this:
const key = this.database.ref('/users').push().key;
this.database.ref('/users').child(key).set({
busiName: business.busiName
});
When you call push() Firebase generates a new unique location, which is the key starting with -M... in your JSON.
That value is not the UID of the current user, so these rules then don't allow the user to read or write it:
"users": {
"$uid": {
".write": "$uid === auth.uid";
".read": "$uid === auth.uid";
}
}
Instead you should write the data under a node using the user's UID as the key. That'd look something like:
const key = this.database.ref('/users').push().key;
if (firebase.auth().currentUser) {
const key = firebase.auth().currentUser.uid;
this.database.ref('/users').child(key).set({
busiName: business.busiName
});
}
else {
console.error("No current user while trying to write business name");
}
I have read tons of stackoverflow posts and docs, but I am still missing something. I am using Firebase realtime database. If I set rules to always true (no authentication needed) and remove interceptor everything is working fine.
Once interceptor is added and requests to Firebase are extended with either:
query param ?auth_token=<token here>
header Authorization: Bearer <token here>
I am getting 401 error (Unauthorized request.). Seems like my auth_token is not correct for some reason. Any idea why and how to fix this?
Thank you in advance!
Code below:
Auth service
import 'firebase/auth';
import 'firebase/database';
import { Injectable } from '#angular/core';
import { Router } from '#angular/router';
import * as firebase from 'firebase/app';
import { environment } from './../../environments/environment';
#Injectable({
providedIn: 'root'
})
export class AuthService {
public user: firebase.User;
public token: string;
constructor(private router: Router) {
this.initFirebase();
}
initFirebase() {
firebase.initializeApp({
apiKey: environment.firebase.apiKey,
databaseURL: environment.firebase.databaseURL
});
const auth = firebase.auth();
auth.onAuthStateChanged(firebaseUser => {
this.user = firebaseUser;
if (firebaseUser) {
firebaseUser.getIdToken().then(token => this.token = token);
} else {
this.token = null;
}
});
}
}
Firebase interceptor (tried providing auth token as either: header or query param)
import { HttpHandler, HttpInterceptor, HttpParams, HttpRequest } from '#angular/common/http';
import { Injectable } from '#angular/core';
import { AuthService } from './auth.service';
#Injectable()
export class FirebaseAuthInterceptorService implements HttpInterceptor {
constructor(private auth: AuthService) {}
intercept(request: HttpRequest<any>, next: HttpHandler) {
if (request.url.indexOf('firebaseio.com') === -1 || !this.auth.user) return next.handle(request);
const params = new HttpParams();
params.append('access_token', this.auth.token);
const newRequest = request.clone({
// headers: request.headers.append('Authorization', 'Bearer ' + this.auth.token)
setParams: {
access_token: this.auth.token
}
});
return next.handle(newRequest);
}
}
I'm working on authentication the project works fine and connect to the database in the firebase, because the project had parts when I need to retrieve data from it, when I try to login with right email and password I get the current error:
Error: A network error (such as timeout, interrupted connection or unreachable host) has occurred.
the loginAdminService:
import { Injectable } from '#angular/core';
import { AngularFireDatabase } from 'angularfire2/database';
import { AngularFireAuth } from "#angular/fire/auth";
import * as fireBase from 'firebase';
#Injectable({
providedIn: 'root'
})
export class LoginAdminserviceService {
isAuth: boolean;
constructor(private angularFireAuth: AngularFireAuth,loginDatabase: AngularFireDatabase) {
}
async login(email: string, password: string) {
return new Promise(
(resolve,reject)=>{
fireBase.auth().signInWithEmailAndPassword(email,password).then(
()=>{
this.isAuth = true;
resolve();
},
(error)=>{
this.isAuth = false;
reject(error);
}
)
}
)
}
async logout() {
return await this.angularFireAuth.auth.signOut();
}
isUserLoggedIn() {
return JSON.parse(localStorage.getItem('user'));
}
}
the authentication component:
import { Component, OnInit } from '#angular/core';
import { LoginAdminserviceService } from '../services/login-adminservice.service';
import { Route, Router } from '#angular/router';
#Component({
selector: 'app-authentication',
templateUrl: './authentication.component.html',
styleUrls: ['./authentication.component.css']
})
export class AuthenticationComponent implements OnInit {
constructor(private route:Router , public loginServiceasAdmin : LoginAdminserviceService) { }
ngOnInit() {
}
async loginAdmin(email:string,password:string){
this.loginServiceasAdmin.login(email,password).then(
()=>{
alert('Bienvenue '+email);
this.route.navigate(['/listreclamation']);
},
(error)=>{
console.log('Pas de connexion '+error);
alert('Votre compte est incorrect');
});
}
}
the html page:
<form>
Email:<input type="text" #email><br>
Password:<input type="password" #password><br>
<button type="submit" (click)="loginAdmin(email.value,password.value)">Login as Admin</button>
<button type="submit" (click)="this.loginServiceasAdmin.logout()">Logout</button>
</form>
Simple example how login service should looks like:
export class AuthService {
user$: Observable<firebase.User>
constructor(private afAuth: AngularFireAuth) {
this.user$ = this.syncUser()
}
// function trigered once are listen when user is logged in.
syncUser() {
return this.afAuth.authState.pipe(
switchMap(user => {
if(user){
return of(user)
} else {
return of(null)
}
})
)
}
// return is not necesery you can allways listen in real time user$ variable.
async signInWith(credentials: IdCredentials) {
const result = await this.afAuth.auth.signInWithEmailAndPassword(credentials.email, credentials.password)
return result
}
}
Make sure you that in your module fire your use emulators Url is as follows:
['http://localhost:<portnumber>', <portnumber>]
and not
['http://localhost', <portnumber>]
I am working on an angular-application with authentication. The application has a dashboard which shows the username of the logged in user. The problem is, that when you login with a new user, it still shows the username of the last logged in user.
So it doesen't update the username (observable) when a user logs in.
I think the problem is that I get the username in the ngOnInit methode.
How can I update all user related data?
header.component.ts (username should be shown)
import { Component, OnInit } from '#angular/core';
import { AuthService } from '../../../core/auth.service';
import { Apollo } from 'apollo-angular';
import gpl from 'graphql-tag';
const registeredUser = gpl`
query registeredUser {
registeredUser {
name
}
}
`
#Component({
selector: 'dashboard-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.css']
})
export class HeaderComponent implements OnInit {
private user$;
constructor(
private authService : AuthService,
private apollo : Apollo) { }
ngOnInit() {
this.user$ = this.apollo.watchQuery<any>({
query: registeredUser
})
.valueChanges;
}
logout() {
this.authService.logout();
}
}
AuthService
import { Injectable } from '#angular/core';
import { JwtHelper } from 'angular2-jwt';
import { Apollo } from 'apollo-angular';
import gpl from 'graphql-tag';
import { Router } from '#angular/router';
const register = gpl`
mutation($name: String!, $email_mobile: String!, $password: String!) {
register(name: $name, email_mobile: $email_mobile, password: $password) {
success
token
user {
name
email
mobile
}
errors {
message
key
}
}
}
`;
const login = gpl`
mutation($email_mobile: String!, $password: String!) {
login(email_mobile: $email_mobile, password: $password) {
success
token
user {
name
email
mobile
}
errors {
message
key
}
}
}
`;
#Injectable()
export class AuthService {
constructor(
private jwtHelper: JwtHelper,
private apollo: Apollo,
private router: Router) { }
isLoggedIn(): boolean {
const token = localStorage.getItem('token');
if(!token)
return false;
// Check whether the token is expired and return
// true or false
return !this.jwtHelper.isTokenExpired(token);
}
async register(name: string, email_mobile: string, password: string) {
let regInfo = {};
await this.apollo.mutate({
mutation: register,
variables: {
name,
email_mobile,
password
}
}).subscribe(({data}) => {
const regData = data.register;
if(regData.success) {
// set token to Local Storage
localStorage.setItem("token", regData.token);
this.router.navigate(['/dashboard']);
} else {
regInfo["errors"] = regData.errors;
}
regInfo["success"] = regData.success;
});
return regInfo;
}
async login(email_mobile: string, password: string) {
let regInfo = {};
await this.apollo.mutate({
mutation: login,
variables: {
email_mobile,
password
}
}).subscribe(({data}) => {
const regData = data.login;
if(regData.success) {
// set token to Local Storage
localStorage.setItem("token", regData.token);
regInfo["user"] = regData.user;
this.router.navigate(['/dashboard']);
} else {
regInfo["errors"] = regData.errors;
}
regInfo["success"] = regData.success;
});
return regInfo;
}
logout(){
localStorage.removeItem("token");
}
}
You should subscribe to changes in your Apollo class, not just take them one off. Your code retrieves the variable, but does not stay to listen in on the pipe, when another user flies by.
Your code
this.user$ = this.apollo.watchQuery<any>({
query: registeredUser
})
.valueChanges;
What I envision
this.apollo.valueChanges.subscribe(
({ data }) => {
this.user$ = [...data.user];
}
);
And remember kids, please unsusbcribe from your pipes!
Read more about GraphQL Subscriptions here!
https://alligator.io/angular/graphql-subscriptions/
I'm using AngularFire2 for an app and I've gotten the registration/login functionality working with Firebase, however, every time I refresh the page, the logged in state is reset and won't persist. I can't quite find functionality to do this, though I feel I'm missing something very small.
Can I use the AngularFireAuth to check on page load somewhere?
Here is my auth provider code:
import { Injectable } from '#angular/core';
import {Observable, Subject, BehaviorSubject} from "rxjs/Rx";
import {AngularFireAuth, FirebaseAuthState} from "angularfire2";
import {AuthInfo} from "./auth-info";
import {Router} from "#angular/router";
#Injectable()
export class AuthService {
static UNKNOWN_USER = new AuthInfo(null);
authInfo$: BehaviorSubject<AuthInfo> = new BehaviorSubject<AuthInfo>(AuthService.UNKNOWN_USER);
constructor(private auth: AngularFireAuth, private router:Router) {
}
login(email, password):Observable<FirebaseAuthState> {
return this.fromFirebaseAuthPromise(this.auth.login({email, password}));
}
signUp(email, password) {
return this.fromFirebaseAuthPromise(this.auth.createUser({email, password}));
}
fromFirebaseAuthPromise(promise):Observable<any> {
const subject = new Subject<any>();
promise
.then(res => {
const authInfo = new AuthInfo(this.auth.getAuth().uid);
this.authInfo$.next(authInfo);
subject.next(res);
subject.complete();
},
err => {
this.authInfo$.error(err);
subject.error(err);
subject.complete();
});
return subject.asObservable();
}
logout() {
this.auth.logout();
this.authInfo$.next(AuthService.UNKNOWN_USER);
this.router.navigate(['/login']);
}
}
Thankyou in advance!
AngularFireAuth is an observable and emits FirebaseAuthState values. If a user is signed in and the page is refreshed, AngularFireAuth will emit an authenticated FirebaseAuthState; otherwise, it will emit null.
So something like this should come close to solving your problem:
constructor(private auth: AngularFireAuth, private router:Router) {
auth.subscribe((authState) => {
if (authState) {
const authInfo = new AuthInfo(authState.uid);
this.authInfo$.next(authInfo);
}
});
}