create account

How to authenticate users via SteemConnect on the Firebase? by jakipatryk

View this thread on: hive.blogpeakd.comecency.com
· @jakipatryk · (edited)
$56.60
How to authenticate users via SteemConnect on the Firebase?
#### What will you learn?

- how OAuth2 authorization code grant works
- how to use [steemconnect-firebase-functions](https://github.com/jakipatryk/steemconnect-firebase-functions) library to set up user authentication via SteemConnect in the Firebase Cloud Functions
- how to build simple frontend with Angular and AngularFire2 to complete OAuth2 code flow and make operations on the Firebase Cloud Firestore database   

#### Requirements

- TypeScript knowledge
- some experience with Angular
- [Angular CLI](https://github.com/angular/angular-cli#installation) installed

#### Difficulty

- basic

# How to authenticate users via SteemConnect on the Firebase?

**After [introducing steemconnect-firebase-functions](https://utopian.io/utopian-io/@jakipatryk/steemconnect-on-the-google-firebase-introducing-steemconnect-firebase-functions) library, obvious next step is a tutorial how to use it. The process of building the [app](https://sc-firebase-functions-example.firebaseapp.com/) we are going to work on today will give you the necessary knowledge to start building your own Steem-related projects on the Firebase. And you will understand OAuth2!**

![User authentication on the Firebase via SteemConnect](https://res.cloudinary.com/hpiynhbhq/image/upload/v1519057472/dlpf13h9vlwpavnzd3th.png)

## What is Firebase?

Firebase is a platform which **gives you the ability to create applications** that use database or authentication **without writing any backend code**...

Well, this is true as long as we don't want to implement an auth system based on SteemConnect :) Firebase Authentication comes with a few auth providers built-in, such as Facebook or Twitter, but **if we want to use SteemConnect we will have to write some backend code**.

The backend code runs on the **Cloud Functions** triggerable by events  such as **HTTP requests**.

**Important note**: if you want to use Firebase on production (it's about deploying Cloud Functions), you will have to choose a paid pricing plan. Why? Our **Cloud Functions are going to make requests to external API** (SteemConnect) and **this kind of operations are not included in the free** (Spark) **plan**. Fortunately, **we still can run Functions locally**, so to follow this tutorial you won't have to pay anything. For more information check [pricing](https://firebase.google.com/pricing/).

## What is OAuth2?

Have you ever signed into any app using your **external account**, maybe the one you have on Google or Facebook? You click 'Login with Facebook' and then the popup window is being opened. You see Facebook which asks you if you want to give this third-party app access to the listed resources. You probably accept it and then magically you become logged in to the app.

You are the **resource owner**, you operate via **user-agent** (in this case a browser) and the application that wants you to give it an access to resources is the **client**.

This process is possible thanks to **OAuth2** -  an authorization framework which enables third-party applications to get limited access to the service.

It's all about the **access tokens**. These strings are used to access resources protected by the service and the entire **OAuth2 flow** is oriented on getting them. The protected resource can be almost anything, from user data to ability to broadcast operations such as publishing a post.

Even though there are a few  OAuth2 flows, all of them are similar due to the fact that  access token is the end goal.

### Authorization Code Grant

The type of OAuth2 flow we will use in this tutorial is authorization code grant. This flow is split into two parts:
- **authentication of the resource owner** - purple arrows on the diagram below
- **getting access token** - blue arrows

![OAuth2 diagram](https://res.cloudinary.com/hpiynhbhq/image/upload/v1519172710/y4qjqs7xddhdvaf0j0r0.png)


This process can be tricky, so let me explain each step:
1. **Resource owner** (via **user-agent**) opens a popup window which **redirects** him to the Firebase Cloud Function.
2. The Firebase Cloud Function **redirects to SteemConnect endpoint** with the client (application) information along with the scope of resources client wants to gain access to, and URI which SteemConnect will redirect to after success. At this point, the user is asked to give the third-party app access to the requested resources.
3. SteemConnect redirects to provided URI with **code** as a query parameter (for example, `http://localhost:4200/redirect?code=fdt453534fds`).
4. **User-agent** on the resource owner behalf makes a **request** to the Firebase Cloud Function along with the **code**.
5. Firebase Cloud Function **makes a request to SteemConnect token endpoint** with client information and the code.
6. **SteemConnect responses with an access token** and some additional info such as the resource owner username.
7. Based on the username **Firebase mints a custom auth token** and sends it back to the resource owner. This step is not really OAuth2 related, but important for the Firebase Authentication system.

Don't worry if you don't understand everything yet. It will become clear when we actually implement these steps in our app.

## Creating Firebase project

The [app](https://sc-firebase-functions-example.firebaseapp.com/) we are going to create is gonna use **Firebase Authentication**, **Firebase Cloud Functions** and **Firebase Cloud Firestore**... so we have to create Firebase project.

This process is really simple, one just has to go to the [Firebase Console](https://console.firebase.google.com/u/0/) and click *Add project*. You will see a modal, where you choose your project name and your country. The last step for now is to click *Create project* and wait a few seconds.

That's all for now, but we will come back here a few times later.

## Creating SteemConnect project

Creating SteemConnect project is nothing fancy once again. To do so, one simply has to go to https://steemconnect.com/dashboard and navigate to *My apps* and click *New app*.

**Warning**: the username for your app you choose at this point is not changeable! So make sure you typed it correctly before clicking *Create account*.

In the next step, fill the form as you wish, but make sure to add `http://localhost:4200/redirect` to *Redirect URI(s)* section:

![SteemConnect project](https://res.cloudinary.com/hpiynhbhq/image/upload/v1519137017/hi4plxsgi4sfyvgkdadi.png)

We will come back to the dashboard later for **client id** and **client secret**.

## Creating Angular project

Although we won't write any code for the frontend yet, creating an Angular project using Angular CLI is always a good start. Let's do it then!

```cmd
ng new your-project-name
cd your-project-name
```

## Backend

The backend we are going to create is, of course, based on Firebase Cloud Functions. However, before we start coding, we have to install `firebase-tools`:

```cmd
npm install firebase-tools -g
```

Once installed, initialize Firebase Cloud Functions project:

```cmd
firebase init functions
```

During this process you will be asked for a default project for this directory, select project which you have created in the **Creating Firebase project** section. Later, when asked *What language would you like to use to write Cloud Functions*, pick **TypeScript**. Also, you **want** to both *use TSLint to catch probable bugs and enforce style* and *install dependencies with npm now*.

Our backend code is now in the *functions* directory, let's move there:

```cmd
cd functions
```

We will need  some dependencies, first of all [**steemconnect-firebase-functions**](https://www.npmjs.com/package/steemconnect-firebase-functions):

```cmd
npm install steemconnect-firebase-functions --save
```

and [CORS](https://www.npmjs.com/package/cors):

```cmd
npm install cors --save
npm install @types/cors --save-dev
```

Next, go back to [Firebase Console](https://console.firebase.google.com/u/0/). We are going to mint Custom Firebase Auth Tokens, so we need service account credentials:

![service account 1](https://res.cloudinary.com/hpiynhbhq/image/upload/v1519157795/rkhb55hasz0scz4dgu87.png)

and then click *GENERATE NEW PRIVATE KEY*.

![service account 2](https://res.cloudinary.com/hpiynhbhq/image/upload/v1519157964/bjzarw6nawogriqsubpr.png)

Move downloaded file to our backend root folder (*functions*) and rename it to *serviceAccountKey.json*. Also, make sure you **don't expose it** (add this file to .gitignore etc.).

Once done, open your favorite code editor and paste the code below to *src/index.ts*:

```javascript
// IMPORTS

import {
  getAuthorizationUrl,
  getAccessToken,
  mintFirebaseToken
} from 'steemconnect-firebase-functions';

import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';

import * as CORS from 'cors';

const serviceAccount = require('../serviceAccountKey.json');

// CONFIGURATION

const cors = CORS({ origin: true });

admin.initializeApp({
  credential: admin.credential.cert(serviceAccount)
});

const redirectUri = 'http://localhost:4200/redirect';
const scope = ['login'];

const clientId = 'YOUR_CLIENT_ID';
const clientSecret = 'YOUR_CLIENT_SECRET';

// FUNCTIONS

export const redirect = functions.https.onRequest((req, res) => {
  const endpoint = getAuthorizationUrl(clientId, redirectUri, scope);
  res.redirect(endpoint);
});

export const callback = functions.https.onRequest((req, res) => {
  cors(req, res, () => {
    handleCallback(req)
      .then(token => res.status(200).send({ token }))
      .catch(err => res.status(400).send(err));
  });
});

// HELPER FUNCTION

async function handleCallback(req) {
  const code = req.query.code;

  const accessToken = await getAccessToken(
    clientId,
    clientSecret,
    redirectUri,
    code
  );
  const uid = `steemconnect:${accessToken.username}`;

  const firebaseToken = await mintFirebaseToken(admin, uid);

  return firebaseToken;
}
```

What's going on there?

#### Imports

Nothing fancy there, we are just importing modules we will need later in this file. The worth to mention is fact that we import **only three functions** from **steemconnect-firebase-functions**, but **there are more of them**, and **will be even more** ;)

We just don't need more for this app - we will only identify user identity and then mint custom auth token based on his username.

#### Configuration

In this section you have to paste your own `clientId` and `clientSecret`. To get them, go back to [SteemConnect dashboard](https://steemconnect.com/dashboard), then go to *My apps* and click on your app name. There you have both of these values.

Also, make sure you don't expose this file - **client secret is a sensitive value**! We paste them as they are only for the purpose of local running. If you decide to deploy your app you will have another way to insert them into your code. More information in the last section of this post.

#### Functions

This is the part of the file where magic happens. We have defined two Cloud Functions - **redirect** and **callback**.  Both react to any **http requests**. It means that each time we make a request or simply navigate to URL dedicated to each function, the code inside it is gonna run.

The **redirect** function is responsible for handling the **step 2** of OAuth2 Authorization Code Grant (see diagram above). It uses `getAuthorizationUrl` function from **steemconnect-firebase-functions** library.

On the other hand, the **callback** function doesn't handle just one step. It actually handles two - **step 5 and 7**. To do so, it uses a helper async function **handleCallback**.

#### Helper function

The **handleCallback** async function uses two functions from **steemconnect-firebase-functions** library - `getAccessToken` (**step 5**) and `mintFirebaseToken` (**step 7**).

It's quite obvious what they do, but fortunately you don't have to worry how to implement functionality they provide. Cool, isn't it?

Also, at this point, you have an access to **access token**. Based on the scope, access token enables client to do different operations on the behalf of the user.

So, in this helper function, we also could save this token in the Cloud Firestore for a later use - `saveAccessToken` function from **steemconnect-firebase-functions** would do the job, but make sure to **deny access to each document of the *steemconnectToken* collection** later with the Firestore security rules! More about them in a separate section and in the references.

We could also get more user details than just a username using the `getUserData` function, again from my library.

Broadcasting operations to the blockchain isn't anything fancy too - as we have access token at this point (remember it requires different scope than *login*) we could use the `broadcastOperations` function. 

### Running functions locally

We have almost everything set up to run our backend locally. There are actually two more commands you have to run in the backend root folder:

```cmd
npm run build
```

and then:

```cmd
firebase serve --only functions
```

Keep them running, because we will need them in the frontend app, which we are going to start building right now.

## Frontend

Our frontend application has two important roles in the OAuth2 flow - handle **step 1** and **step  4**.  In addition, it has to make use of the token that **callback** Cloud Function responses with if minting custom auth token succeed. Quite a lot of work to do, so let's start!

If you are in the *functions* directory, go back to Angular project:

```cmd
cd ../
```

but make sure functions are still running!

The first step in building Firebase frontend in Angular is usually configuring [AngularFire2](https://github.com/angular/angularfire2):

```cmd
npm install angularfire2 firebase --save
```

once installed, go back to [Firebase Console](https://console.firebase.google.com/u/0/), choose your project and click *Add Firebase to your web app*. Copy all properties of config object and paste them inside *src/environments/environment.ts* file like that:

```javascript
export const environment = {
  production: false,
  firebaseConfig: {
    apiKey: '<your-key>',
    authDomain: '<your-project-authdomain>',
    databaseURL: '<your-database-URL>',
    projectId: '<your-project-id>',
    storageBucket: '<your-storage-bucket>',
    messagingSenderId: '<your-messaging-sender-id>'
  }
};
```

We also have to import a few AngularFire2 modules. Our AppModule should look like that for now:

```javascript
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AngularFireModule } from 'angularfire2';
import { AngularFireAuthModule } from 'angularfire2/auth';
import { AngularFirestoreModule } from 'angularfire2/firestore';

import { AppComponent } from './app.component';

import { environment } from './../environments/environment';

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    AngularFireModule.initializeApp(environment.firebaseConfig),
    AngularFireAuthModule,
    AngularFirestoreModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {}
```

Next let's create an AuthService, which will handle most of the user authentication:
```cmd
ng g s auth -m app
```

now fill *auth.service.ts* with the following code:

```javascript
import { Injectable } from '@angular/core';

import { Observable } from 'rxjs/Observable';
import { switchMap } from 'rxjs/operators';

import { AngularFireAuth } from 'angularfire2/auth';

@Injectable()
export class AuthService {
  user: Observable<any>;

  constructor(private afAuth: AngularFireAuth) {
    this.user = this.afAuth.authState;
  }

  login() {
    const popup = window.open(
      'redirect_function_url',
      '_blank',
      'height=700,width=800'
    );
  }

  signIn(token) {
    return this.afAuth.auth
      .signInWithCustomToken(token)
      .then(() => window.close());
  }

  signout() {
    this.afAuth.auth.signOut();
  }
}
```

and replace `redirect_function_url` in the `login` method with the actual URL to **redirect Function**.

Before we create components to complete our OAuth2 flow, let's ensure it will look pretty. [Bootstrap](https://getbootstrap.com/) seems like a good choice, let's add it to *src/index.html*:

```html
<!doctype html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <title>SteemconnectFirebaseFunctionsExample</title>
  <base href="/">

  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">

  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
    crossorigin="anonymous">
</head>

<body>
  <app-root></app-root>
</body>

</html>
```

At this point, we need two more components - **RedirectComponent** and **UserDetailsComponent**. The first one will handle the **step 4** and the second one will be responsible to provide the functionality of **step 1**.

**RedirectComponent** will be created each time user hit the `http://localhost:4200/redirect` URL. To be more precise - user won't navigate there, but SteemConnect will redirect there after successful login with the **code** as a query parameter, and the job of RedirectComponent will be to deliver this code to **callback** Cloud Function and make a use of its response - custom auth token.

On the other hand, **UserDetailsComponent** will display a *Login with SteemConnect* button for not logged in users, and **uid** along with *Signout* button for logged in users.

Let's generate them:

```cmd
ng g c redirect
ng g c user-details
```

The *redirect.component.ts* should look like that:

```javascript
import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Router, ActivatedRoute, Params } from '@angular/router';

import { switchMap } from 'rxjs/operators';
import { fromPromise } from 'rxjs/observable/fromPromise';

import { AuthService } from '../auth.service';

@Component({
  selector: 'app-redirect',
  templateUrl: './redirect.component.html',
  styleUrls: ['./redirect.component.css']
})
export class RedirectComponent implements OnInit {
  constructor(
    private http: HttpClient,
    private route: ActivatedRoute,
    private auth: AuthService
  ) {}

  ngOnInit() {
    const code = this.route.snapshot.queryParamMap.get('code');
    const url = `<CALLBACK-FUNCTION-URL>/callback?code=${code}`;

    if (code) {
      this.http
        .post<any>(url, {})
        .pipe(
          switchMap(res => {
            return fromPromise(this.auth.signIn(res.token));
          })
        )
        .subscribe();
    }
  }
}
```

Make sure to replace `<CALLBACK-FUNCTION-URL>` with the actual URL of the **callback** Function. 

Both *redirect.component.html* and *redirect.component.css* are not that important, because this component will be displayed only for short period of time, so you can leave it as it is, change text in the HTML to something like *Loading...*, or even make a fancy loading spinner on your own :)

Now let's move to **UserDetailsComponent**; *user-details.component.ts* is simple:

```javascript
import { Component } from '@angular/core';

import { AuthService } from './../auth.service';

@Component({
  selector: 'app-user-details',
  templateUrl: './user-details.component.html',
  styleUrls: ['./user-details.component.css']
})
export class UserDetailsComponent {
  constructor(public auth: AuthService) {}
}
```

the entire magic happens in its template *user-details.component.html*:

```html
<div *ngIf="auth.user | async as user; else guest">
  <div class="card">
    <div class="card-body">
      <h5 class="card-title">Hello!</h5>
      <h6 class="card-subtitle mb-2 text-muted">You are logged into Firebase via SteemConnect!</h6>
      <p class="card-text">That's your uid:
        <b>{{ user.uid }}</b>
      </p>
      <button (click)="auth.signout()" class="btn btn-warning">Signout</button>
    </div>
  </div>
</div>

<ng-template #guest>
  <button (click)="auth.login()" class="btn btn-primary">Login with SteemConnect!</button>
</ng-template>
```

To make signing in feature work, we need to do one more thing - set up routing. Let's start with the *app.component.html*, where `router-outlet` is gonna be placed:

```html
<nav class="navbar navbar-expand-lg navbar-light bg-light">
  <a class="navbar-brand" href="">steemconnect-firebase-functions</a>
  <div class="navbar-nav">
    <a class="nav-item nav-link" href="https://github.com/jakipatryk/steemconnect-firebase-functions-example">Github</a>
  </div>
</nav>

<div class="container">
  <div class="row">
    <div class="col-xs-12 col-md-4">
      <router-outlet></router-outlet>
    </div>

    <div class="col-xs-12 col-md-8">
      We will add messages here later on.
    </div>
  </div>
</div>
```

The last step for now is configuration of routes in *app.module.ts*, this file should look like following for now:

```javascript
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { HttpClientModule } from '@angular/common/http';

import { AngularFireModule } from 'angularfire2';
import { AngularFireAuthModule } from 'angularfire2/auth';
import { AngularFirestoreModule } from 'angularfire2/firestore';

import { AppComponent } from './app.component';
import { RedirectComponent } from './redirect/redirect.component';
import { UserDetailsComponent } from './user-details/user-details.component';

import { AuthService } from './auth.service';

import { environment } from './../environments/environment';

const routes: Routes = [
  { path: '', component: UserDetailsComponent },
  { path: 'redirect', component: RedirectComponent }
];

@NgModule({
  declarations: [AppComponent, RedirectComponent, UserDetailsComponent],
  imports: [
    BrowserModule,
    RouterModule.forRoot(routes),
    HttpClientModule,
    AngularFireModule.initializeApp(environment.firebaseConfig),
    AngularFireAuthModule,
    AngularFirestoreModule
  ],
  providers: [AuthService],
  bootstrap: [AppComponent]
})
export class AppModule {}
```

Aaaand... we have implemented the entire OAuth2 Authorization Code Grant and the signing in with the Firebase custom auth token! Don't you believe? Ok, let's test it then!

```cmd
ng serve
```

now just open up your browser and navigate to `http://localhost:4200`, that's what you are gonna see after successful sign in:

![OAuth2 completed](https://res.cloudinary.com/hpiynhbhq/image/upload/v1519227526/zoricn9g0mthlyu0pqyb.png)

also, the *Authentication* page in the Firebase Console has changed ;)

![Firebase Console Authentication](https://res.cloudinary.com/hpiynhbhq/image/upload/v1519227586/agedzxfqgsea3hnnhskd.png)

I could have ended this tutorial here, but I want to show a really trivial example of what you can do now. Even though it is not gonna be complex, you will see that from this point making Steem-related apps that doesn't add anything to the blockchain is easy. Do you wonder why would anyone create an Steem-related app that don't publish anything to the blockchain? Check [SteemProjects](https://steemprojects.com/) by @noisy.

### Messages

In this section we will add messages that any logged in user would be able to add. At the end our app will look like the [example app](https://sc-firebase-functions-example.firebaseapp.com/).

Before we move to the code, you have to turn on Cloud Firestore. To do so, simply go to the [Firebase Console](https://console.firebase.google.com/u/0/), choose your project, go to *Database* page and click *TRY FIRESTORE BETA* (select *Start in test mode* when asked).

Let's start with generating two presentational components - **MessageDetailsComponent** and **MessageFormComponent**, one container component - **MessageListComponent**, a service to handle data flow between the Angular app and the Firestore - **MessageService** and the interface of **Message**:

```cmd
ng g c messages/message-details
ng g c messages/message-form
ng g c messages/message-list
ng g s messages/message -m app
ng g i messages/models/message
```

Our **Message** interface should look like that:

```javascript
export interface Message {
  author: string;
  text: string;
}
```

Now let's bring some life to our presentational components starting with **MessageFormComponent**. This kind of component shouldn't be aware of any data, so in *message-form.component.ts* we should use `@Output` decorator and `EventEmmiter` to emit values of our reactive form once submited:

```javascript
import { Component, OnInit, EventEmitter, Output } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';

import { Message } from '../models/message';

@Component({
  selector: 'app-message-form',
  templateUrl: './message-form.component.html',
  styleUrls: ['./message-form.component.css']
})
export class MessageFormComponent implements OnInit {
  @Output() messageEmitter: EventEmitter<Message> = new EventEmitter<Message>();

  messageForm: FormGroup;

  constructor(private formBuilder: FormBuilder) {}

  ngOnInit() {
    this.createMessageForm();
  }

  addMessage() {
    this.messageEmitter.emit({
      ...this.messageForm.value
    });
  }

  private createMessageForm() {
    this.messageForm = this.formBuilder.group({
      text: ['', Validators.required]
    });
  }
}
```

the template of this component is nothing fancy, just a simple form:

```html
<form [formGroup]="messageForm" (ngSubmit)="addMessage()">
  <div class="form-group">
    <label for="textInput">Your message:</label>
    <input formControlName="text" id="textInput" class="form-control" aria-describedby="textHelper">
    <small id="textHelper" class="form-text text-muted">It's NOT gonna be published on the Steem blockchain.</small>
  </div>
  <button type="submit" class="btn btn-primary">Submit</button>
</form>
```

The second presentational component is going to be responsible to display the details of a message (text, author); *message-details.component.ts*:

```javascript
import { Component, Input } from '@angular/core';
import { Message } from '../models/message';

@Component({
  selector: 'app-message-details',
  templateUrl: './message-details.component.html',
  styleUrls: ['./message-details.component.css']
})
export class MessageDetailsComponent {
  @Input() message: Message;
}
```

and *message-details.component.html*:

```html
<div class="card">
  <div class="card-body">
    <div class="card-title mb-2 text-muted">{{ message.author | uidToUsername }}</div>
    <div class="card-text">{{ message.text }}</div>
  </div>
</div>
```

As you can see, we use `uidToUsername` pipe, which is a custom pipe, so we have to generate it:

```cmd
ng g p messages/pipes/uid-to-username
```

It transforms uid to username, for example **steemconnect:jakipatryk** to **jakipatryk**:

```javascript
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'uidToUsername'
})
export class UidToUsernamePipe implements PipeTransform {
  transform(value: string, args?: any): string {
    const username = value.replace('steemconnect:', '');
    return username;
  }
}
```

We are almost ready to power up our presentational components with data. Yes, almost. We have to complete two more tasks. Updating **MessageService** is a perfect choice now:

```javascript
import { Injectable } from '@angular/core';

import {
  AngularFirestore,
  AngularFirestoreDocument
} from 'angularfire2/firestore';

import { Observable } from 'rxjs/Observable';

import { Message } from './models/message';

@Injectable()
export class MessageService {
  constructor(private afs: AngularFirestore) {}

  addMessage(data: Message) {
    return this.afs.collection('messages').add({ ...data });
  }

  getMessages(): Observable<Message[]> {
    return this.afs.collection('messages').valueChanges() as Observable<Message[]>;
  }
}
```

Now we can use it in our container component - **MessageListComponent**:

*message-list.component.ts*:

```javascript
import { Component, OnInit } from '@angular/core';

import { MessageService } from './../message.service';
import { AuthService } from '../../auth.service';

import { Message } from '../models/message';

import { Observable } from 'rxjs/Observable';

@Component({
  selector: 'app-message-list',
  templateUrl: './message-list.component.html',
  styleUrls: ['./message-list.component.css']
})
export class MessageListComponent implements OnInit {
  currentUser;
  messages: Observable<Message[]>;

  constructor(
    private messageService: MessageService,
    public auth: AuthService
  ) {}

  ngOnInit() {
    this.auth.user.subscribe(user => {
      this.currentUser = user;
    });
    this.messages = this.messageService.getMessages();
  }

  addMessage(data) {
    const dataWithAuthor = { ...data, author: this.currentUser.uid };
    this.messageService.addMessage(dataWithAuthor);
  }
}
```

*message-list.component.html*:

```html
<div *ngIf="auth.user | async as user">
  <app-message-form (messageEmitter)="addMessage($event)"></app-message-form>
</div>


<div *ngIf="!(messages | async)">
  Loading messages...
</div>

<app-message-details *ngFor="let message of messages | async" [message]="message"></app-message-details>
```

We are almost done. Let's now add our container component to **AppComponent**, its template should have the following code:

```html
<nav class="navbar navbar-expand-lg navbar-light bg-light">
  <a class="navbar-brand" href="">steemconnect-firebase-functions</a>
  <div class="navbar-nav">
    <a class="nav-item nav-link" href="https://github.com/jakipatryk/steemconnect-firebase-functions-example">Github</a>
  </div>
</nav>

<div class="container">
  <div class="row">
    <div class="col-xs-12 col-md-4">
      <router-outlet></router-outlet>
    </div>

    <div class="col-xs-12 col-md-8">
      <app-message-list></app-message-list>
    </div>
  </div>
</div>
```

The last step is to make sure we have included everything in the **AppModule**:

```javascript
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { Routes, RouterModule } from '@angular/router';
import { ReactiveFormsModule } from '@angular/forms';

import { AngularFireModule } from 'angularfire2';
import { AngularFireAuthModule } from 'angularfire2/auth';
import { AngularFirestoreModule } from 'angularfire2/firestore';

import { AppComponent } from './app.component';
import { RedirectComponent } from './redirect/redirect.component';
import { UserDetailsComponent } from './user-details/user-details.component';

import { AuthService } from './auth.service';
import { MessageService } from './messages/message.service';

import { environment } from './../environments/environment';
import { MessageListComponent } from './messages/message-list/message-list.component';
import { MessageDetailsComponent } from './messages/message-details/message-details.component';
import { MessageFormComponent } from './messages/message-form/message-form.component';
import { UidToUsernamePipe } from './messages/uid-to-username.pipe';

const routes: Routes = [
  { path: '', component: UserDetailsComponent },
  { path: 'redirect', component: RedirectComponent }
];

@NgModule({
  declarations: [
    AppComponent,
    RedirectComponent,
    UserDetailsComponent,
    MessageListComponent,
    MessageDetailsComponent,
    MessageFormComponent,
    UidToUsernamePipe
  ],
  imports: [
    BrowserModule,
    RouterModule.forRoot(routes),
    HttpClientModule,
    ReactiveFormsModule,
    AngularFireModule.initializeApp(environment.firebaseConfig),
    AngularFireAuthModule,
    AngularFirestoreModule
  ],
  providers: [AuthService, MessageService],
  bootstrap: [AppComponent]
})
export class AppModule {}
```

**Now simply `ng serve` the app and enjoy your first Firebase and SteemConnect based application!**

![Final app](https://res.cloudinary.com/hpiynhbhq/image/upload/v1519239977/ym7exspagi3a1zutlnua.jpg)

## Backend once again (security)

We do already have a working app. Cool, but you might remember that you started the database in the **test mode**. While it is ok to use it during the development phase, it is completely wrong to use this mode on the production.

Why? Test mode allows anyone to read, create, update and delete any data in the Firestore. We don't want that.

We want to allow:
- **anyone to read** messages
- **logged in users to create** messages
- **authors to update or delete** their messages

To achieve this, one has to visit [Firebase console](https://console.firebase.google.com/u/0/) once again, navigate to the project and then to *Database* page. Here, in the *RULES* section, we define our rules. 

In case of our app, the rules should be following:

```
service cloud.firestore {
  match /databases/{database}/documents {
    match /messages/{message} {
      allow read: if true;
      allow update, delete: if request.auth.uid == resource.data.author;
      allow create: if request.auth.uid != null;
    }
  }
}
```

## Deploying the app and Cloud Functions

**NOTE**: this section is for those who decided to choose **paid pricing plan** (Flame or Blaze).

Ok, we have everything set up. Now it's time for deployment. However, we have to do some configuration before we enter deploy command.

Let's start with the frontend. You might remember that we inserted the Firebase configuration in the *environment.ts* file. It worked well, but it won't work on the production. Angular CLI takes different file during the build for production - *environment.prod.ts*.

Basically, all you have to do is to copy `firebaseConfig` object from *environment.ts* to *environment.prod.ts*, but I highly recomment you create another Firebase project just for production. The setup process will be exactly the same as we did before.

Now let's move to backend Cloud Functions. We need to setup clientId and clientSecret as a Firebase variables, so in the terminal move to *functions* directory and type the following commands:

```cmd
firebase functions:config:set steemconnect.id="YOUR_CLIENT_ID" steemconnect.secret="YOUR_CLIENT_SECRET"
```

Both **id** and **secret** are of course the same as before. Now let's change a bit our *index.ts* file, instead of:

```javascript
const clientId = 'YOUR_CLIENT_ID';
const clientSecret = 'YOUR_CLIENT_SECRET';
```

use:

```javascript
const clientId = functions.config().steemconnect.id;
const clientSecret = functions.config().steemconnect.secret;
```

We also have to change the `redirectUri`. To do so, you have to go to [Firebase console](https://console.firebase.google.com/u/0/) once again and then to your project. This time hit the *Hosting* page, click *GET STARTED*, then *CONTINUE* and finally *FINISH*. Copy the **domain** and update `redirectUri`:

```javascript
const redirectUri = 'https://<YOUR-DOMAIN>/redirect';
```

Remember to add this redirectUri to the *Redirect URI(s)* at the **SteemConnect dashboard!**

Now we can deploy our Cloud Functions!

```cmd
firebase deploy --only functions
```

If you are getting an error here, try changing *firebase.json* file from:

```
{
  "functions": {
    "predeploy": [
      "npm --prefix $RESOURCE_DIR run lint",
      "npm --prefix $RESOURCE_DIR run build"
    ]
  }
}
```

to:

```
{
  "functions": {
    "predeploy": [
      "npm --prefix %RESOURCE_DIR% run lint",
      "npm --prefix %RESOURCE_DIR% run build"
    ]
  }
}
```

Once done, copy the URLs of the Cloud Functions and change the URLs in *auth.service.ts* and *redirect.component.ts*.

We could - and I would defienietly do so in a serious app - add additional property to environment files, something like `functionsURL` and add different URLs for production and development, so we could still test our functions and frontend locally. Then we could simply import `environment` file to both of these files and not change the URL directly, but use the environment variable `functionsURL` instead. Angular CLI would do its job then. 

Anyway, whatever option you have chosen, the deployment of the Angular app will look exactly the same. First of all, we have to initialize the **Firebase Hosting**. To do so, move to the root folder of Angular app and type the command:

```cmd
firebase init hosting
```

When asked:
- *What do you want to use as your public directory?* - type **dist**
- *Configure as a single-page app (rewrite all urls to /index.html)?* - type **y**

Once initialized, you have to build your app for production:

```cmd
ng build --prod
```
The last step is to run:

```cmd
firebase deploy --only hosting
```

Now you can enjoy your deployed app and Cloud Functions and share it with your friends!

## Summary

Huh, it's been a long tutorial. We have created an [app](https://sc-firebase-functions-example.firebaseapp.com/) that implements **OAuth2 Authorization Code Grant** to enable users to authenticate via SteemConnect on the Firebase. To achieve this, we used [**steemconnect-firebase-functions**](https://github.com/jakipatryk/steemconnect-firebase-functions/) library, which I had created to make developer's life easier. On the frontend we used **Angular** + **AngularFire2** to build simple but powerful application.

### References

- OAuth2:
   - [The OAuth 2.0 Authorization Framework](https://tools.ietf.org/pdf/rfc6749.pdf)
- SteemConnect:
   - [available scopes](https://github.com/steemit/steemconnect/wiki/OAuth-2#scopes)
- Firebase Cloud Functions:
   - [official docs](https://firebase.google.com/docs/functions/)
- Firebase Cloud Firestore and rules:
   - [Firestore docs](https://firebase.google.com/docs/firestore/)
   - [security rules docs](https://firebase.google.com/docs/firestore/security/overview?authuser=0)
- AngularFire2:
   - [official docs](https://github.com/angular/angularfire2#angularfire)
- user agent
   - [Wikipedia](https://en.wikipedia.org/wiki/User_agent)

### Github

The code of the app we have built in this tutorial is avaiable on the Github:
- https://github.com/jakipatryk/steemconnect-firebase-functions-example

Also, the **steemconnect-firebase-functions** library is on the Github:
- https://github.com/jakipatryk/steemconnect-firebase-functions 

<br /><hr/><em>Posted on <a href="https://utopian.io/utopian-io/@jakipatryk/how-to-authenticate-users-via-steemconnect-on-the-firebase">Utopian.io -  Rewarding Open Source Contributors</a></em><hr/>
👍  , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , and 48 others
properties (23)
authorjakipatryk
permlinkhow-to-authenticate-users-via-steemconnect-on-the-firebase
categoryutopian-io
json_metadata{"community":"utopian","app":"utopian/1.0.0","format":"markdown","repository":{"id":120048095,"name":"steemconnect-firebase-functions","full_name":"jakipatryk/steemconnect-firebase-functions","html_url":"https://github.com/jakipatryk/steemconnect-firebase-functions","fork":false,"owner":{"login":"jakipatryk"}},"pullRequests":[],"platform":"github","type":"tutorials","tags":["utopian-io","steemconnect","steemdev","steemstem","dev"],"users":["jakipatryk","types","angular","NgModule","Injectable","Component","noisy.","Output","Input","Pipe"],"links":["https://github.com/jakipatryk/steemconnect-firebase-functions","https://github.com/angular/angular-cli#installation","https://utopian.io/utopian-io/@jakipatryk/steemconnect-on-the-google-firebase-introducing-steemconnect-firebase-functions","https://sc-firebase-functions-example.firebaseapp.com/","https://res.cloudinary.com/hpiynhbhq/image/upload/v1519057472/dlpf13h9vlwpavnzd3th.png","https://firebase.google.com/pricing/","https://res.cloudinary.com/hpiynhbhq/image/upload/v1519172710/y4qjqs7xddhdvaf0j0r0.png","https://console.firebase.google.com/u/0/","https://res.cloudinary.com/hpiynhbhq/image/upload/v1519137017/hi4plxsgi4sfyvgkdadi.png","https://www.npmjs.com/package/steemconnect-firebase-functions","https://www.npmjs.com/package/cors","https://res.cloudinary.com/hpiynhbhq/image/upload/v1519157795/rkhb55hasz0scz4dgu87.png","https://res.cloudinary.com/hpiynhbhq/image/upload/v1519157964/bjzarw6nawogriqsubpr.png","https://steemconnect.com/dashboard","https://github.com/angular/angularfire2","https://getbootstrap.com/","https://res.cloudinary.com/hpiynhbhq/image/upload/v1519227526/zoricn9g0mthlyu0pqyb.png","https://res.cloudinary.com/hpiynhbhq/image/upload/v1519227586/agedzxfqgsea3hnnhskd.png","https://steemprojects.com/","https://res.cloudinary.com/hpiynhbhq/image/upload/v1519239977/ym7exspagi3a1zutlnua.jpg","https://github.com/jakipatryk/steemconnect-firebase-functions/","https://tools.ietf.org/pdf/rfc6749.pdf","https://github.com/steemit/steemconnect/wiki/OAuth-2#scopes","https://firebase.google.com/docs/functions/","https://firebase.google.com/docs/firestore/","https://firebase.google.com/docs/firestore/security/overview?authuser=0","https://github.com/angular/angularfire2#angularfire","https://en.wikipedia.org/wiki/User_agent"],"image":["https://res.cloudinary.com/hpiynhbhq/image/upload/v1519057472/dlpf13h9vlwpavnzd3th.png","https://res.cloudinary.com/hpiynhbhq/image/upload/v1519172710/y4qjqs7xddhdvaf0j0r0.png","https://res.cloudinary.com/hpiynhbhq/image/upload/v1519137017/hi4plxsgi4sfyvgkdadi.png","https://res.cloudinary.com/hpiynhbhq/image/upload/v1519157795/rkhb55hasz0scz4dgu87.png","https://res.cloudinary.com/hpiynhbhq/image/upload/v1519157964/bjzarw6nawogriqsubpr.png","https://res.cloudinary.com/hpiynhbhq/image/upload/v1519227526/zoricn9g0mthlyu0pqyb.png","https://res.cloudinary.com/hpiynhbhq/image/upload/v1519227586/agedzxfqgsea3hnnhskd.png","https://res.cloudinary.com/hpiynhbhq/image/upload/v1519239977/ym7exspagi3a1zutlnua.jpg"],"moderator":{"account":"sakibarifin","time":"2018-02-23T00:43:08.234Z","reviewed":true,"pending":false,"flagged":false},"questions":[],"score":0}
created2018-02-22 13:23:45
last_update2018-02-23 00:43:09
depth0
children5
last_payout2018-03-01 13:23:45
cashout_time1969-12-31 23:59:59
total_payout_value41.968 HBD
curator_payout_value14.627 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length38,679
author_reputation14,313,610,947,295
root_title"How to authenticate users via SteemConnect on the Firebase?"
beneficiaries
0.
accountutopian.pay
weight2,500
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id39,607,995
net_rshares12,654,103,601,799
author_curate_reward""
vote details (112)
@sakibarifin ·
$1.19
Thank you for the contribution. It has been approved.

You can contact us on [Discord](https://discord.gg/uTyJkNm).
**[[utopian-moderator]](https://utopian.io/moderators)**
👍  ,
properties (23)
authorsakibarifin
permlinkre-jakipatryk-how-to-authenticate-users-via-steemconnect-on-the-firebase-20180223t004310785z
categoryutopian-io
json_metadata{"tags":["utopian-io"],"community":"utopian","app":"utopian/1.0.0"}
created2018-02-23 00:43:18
last_update2018-02-23 00:43:18
depth1
children1
last_payout2018-03-02 00:43:18
cashout_time1969-12-31 23:59:59
total_payout_value0.897 HBD
curator_payout_value0.294 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length172
author_reputation2,433,294,475,856
root_title"How to authenticate users via SteemConnect on the Firebase?"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id39,731,622
net_rshares214,451,609,736
author_curate_reward""
vote details (2)
@utopian.tip ·
Hey @sakibarifin, I just gave you a tip for your hard work on moderation. Upvote this comment to support the utopian moderators and increase your future rewards!
properties (22)
authorutopian.tip
permlinkre-re-jakipatryk-how-to-authenticate-users-via-steemconnect-on-the-firebase-20180223t004310785z-20180223t235906
categoryutopian-io
json_metadata""
created2018-02-23 23:59:06
last_update2018-02-23 23:59:06
depth2
children0
last_payout2018-03-02 23:59:06
cashout_time1969-12-31 23:59:59
total_payout_value0.000 HBD
curator_payout_value0.000 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length161
author_reputation238,310,597,885
root_title"How to authenticate users via SteemConnect on the Firebase?"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id39,982,149
net_rshares0
@steemstem-bot ·
$0.85
<center><a href="www.steemit.com/@steemstem"><img src="https://media.discordapp.net/attachments/384404201544876032/405507994583957505/steemSTEM.png"></a><br><table><tr><th> </th><th> </th><th><a href="https://steemit.com/steemstem/@steemstem/helpful-guidelines-for-crafting-steemstem-content">Guidelines</a></th><th><a href="https://steemit.com/steemstem/@steemstem/steemstem-winter-2017-2018-project-update">Project Update</a></th><th> </th><th> </th></tr></table><br><a href="https://steemit.com/steemstem/@steemstem/being-a-member-of-the-steemstem-community"><b>Being A SteemStem Member</b></a></center>
👍  ,
properties (23)
authorsteemstem-bot
permlinkre-how-to-authenticate-users-via-steemconnect-on-the-firebase-20180223t051458
categoryutopian-io
json_metadata""
created2018-02-23 05:14:57
last_update2018-02-23 05:14:57
depth1
children0
last_payout2018-03-02 05:14:57
cashout_time1969-12-31 23:59:59
total_payout_value0.848 HBD
curator_payout_value0.003 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length606
author_reputation3,811,533,615,496
root_title"How to authenticate users via SteemConnect on the Firebase?"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id39,778,033
net_rshares153,337,155,802
author_curate_reward""
vote details (2)
@utopian-1up ·
<div class="pull-left">

![1UP-Kayrex_tiny.png](https://res.cloudinary.com/hpiynhbhq/image/upload/v1515383984/ekyf2thxg7j2t0qro1h3.png)

</div>

<div class="text-justify">


### You've got upvoted by <code>Utopian-1UP</code>!
You can give up to ten [1UP](https://steemit.com/utopian-io/@steem-plus/steemplus-2-4-utopian-1up-is-here)'s to Utopian posts every day after they are accepted by a Utopian moderator and before they are upvoted by the official @utopian-io account. Install the @steem-plus browser extension to use 1UP. By following the 1UP-trail using [SteemAuto](https://steemauto.com/) you support great Utopian authors and earn high curation rewards at the same time. 

<hr>

1UP is neither organized nor endorsed by Utopian.io!

</div>
properties (22)
authorutopian-1up
permlink20180302t091830420z
categoryutopian-io
json_metadata{"app":"1up"}
created2018-03-02 09:18:30
last_update2018-03-02 09:18:30
depth1
children0
last_payout2018-03-09 09:18:30
cashout_time1969-12-31 23:59:59
total_payout_value0.000 HBD
curator_payout_value0.000 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length764
author_reputation2,324,758,056,093
root_title"How to authenticate users via SteemConnect on the Firebase?"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id41,592,198
net_rshares0
@utopian-io ·
### Hey @jakipatryk I am @utopian-io. I have just upvoted you!
#### Achievements
- You have less than 500 followers. Just gave you a gift to help you succeed!
- You are generating more rewards than average for this category. Super!;)
- Seems like you contribute quite often. AMAZING!
#### Suggestions
- Contribute more often to get higher and higher rewards. I wish to see you often!
- Work on your followers to increase the votes/rewards. I follow what humans do and my vote is mainly based on that. Good luck!
#### Get Noticed!
- Did you know project owners can manually vote with their own voting power or by voting power delegated to their projects? Ask the project owner to review your contributions!
#### Community-Driven Witness!
I am the first and only Steem Community-Driven Witness. <a href="https://discord.gg/zTrEMqB">Participate on Discord</a>. Lets GROW TOGETHER!
- <a href="https://v2.steemconnect.com/sign/account-witness-vote?witness=utopian-io&approve=1">Vote for my Witness With SteemConnect</a>
- <a href="https://v2.steemconnect.com/sign/account-witness-proxy?proxy=utopian-io&approve=1">Proxy vote to Utopian Witness with SteemConnect</a>
- Or vote/proxy on <a href="https://steemit.com/~witnesses">Steemit Witnesses</a>

[![mooncryption-utopian-witness-gif](https://steemitimages.com/DQmYPUuQRptAqNBCQRwQjKWAqWU3zJkL3RXVUtEKVury8up/mooncryption-s-utopian-io-witness-gif.gif)](https://steemit.com/~witnesses)

**Up-vote this comment to grow my power and help Open Source contributions like this one. Want to chat? Join me on Discord https://discord.gg/Pc8HG9x**
properties (22)
authorutopian-io
permlinkre-jakipatryk-how-to-authenticate-users-via-steemconnect-on-the-firebase-20180223t091357671z
categoryutopian-io
json_metadata{"tags":["utopian-io"],"community":"utopian","app":"utopian/1.0.0"}
created2018-02-23 09:13:57
last_update2018-02-23 09:13:57
depth1
children0
last_payout2018-03-02 09:13:57
cashout_time1969-12-31 23:59:59
total_payout_value0.000 HBD
curator_payout_value0.000 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length1,583
author_reputation152,955,367,999,756
root_title"How to authenticate users via SteemConnect on the Firebase?"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id39,821,661
net_rshares0