Few days ago, I’ve planned to design some side project, which is based on mobile application. This is the record about how I’ve implement authentication logic through application side to back-end side.
I’ve once wrote about authentication with golang, but in this time I’ve decided to use nodejs. Still yet golang was not handy to me, and because I’ve changed my position on work, it requires much time to get used on new work(and studying new programming language).
Moreover, I’ve decided use Flutter, which I’ve never used before, so couldn’t have much time to assign a time for get used to golang.
Why Flutter?
If the performance is not a high priority for application, it’s inconvenient to develop apps for iOS and Android separately. Luckly there are several ways to develop application for iOS/Android in same code base.
There are other frameworks(Xamarin, React-Native) to make this done, but lots of performance benchmark(it’s pretty close to native!), growth of community, and support from Google make me to choose Flutter. Also, there were similarities with ‘component’ based development at front-end.
One sad thing(maybe good thing) is there are no other choice than using Dart
for development, but thought it worths to go on with this.
Why Nestjs
NestJS is one of web framework based on NodeJS, like Express, Koa. It has rapidly grow-up in few years and become one of most trendy project, by offering user to build modulized, looseley coupled architecture with less effort.
Check the small piece of code:
|
|
As you can see, it actively supports Decorator
for clean code. Maybe you can remind of Spring
framework in Java, or Angular
framework. It also supports TypeScript by default, for type-safety application.
I was using Express for years but had chance to use this for few month, and impressed in development efficiency. Now I’m not with NodeJS, but wanted to get more deeply with this framework, by apply in this personal project.
Authentication logic for Flutter
These are the logic for basic authentication.
- Open application
- Check state of authentication with stored JWT
- If JWT is available, show main page
- If not, move to login page
- In 2-2, login with ID/PW, and store JWT to local storage
You can just call API for each process and check state separately, but let’s try to use ‘provider pattern’ to keep state in unified logic.
Define API
First let’s define APIs to get/post login informations from server.
|
|
I’ve used wrapper class for response handler(ApiResponse, ApiError). About this, you can find more in here. It is one kind of method to keep common rule for response handling. This post will only focus on managing login state.
There are 2 APIs here:
- ${_baseUrl}api/auth/login -> login with ID/PW
- ${_baseUrl}api/users/profile -> get profile with token stored in device. It is to check user is logged in or not.
with provider pattern
Provider pattern is one of design pattern, to manage application state. In this pattern, states are being managed in separate module, and views which requires these information will check states from this module. If you’re familiar with react-redux
or vuex
, you can understand more quickly.
For implementation, you need to add provider
module.
|
|
First, let’s define ‘state’ of login. Basically, you can think of ’loading’, ‘success’, ‘fail’, but you can divide into more segments if needed.
|
|
Now will make provider class. For this, you need to extend ChangeNotifier
. This is a simple class included in the Flutter SDK which provides change notification to its listeners. By extending this, user can subscribe to its change.
|
|
First, you can see line LoginState _state = LoginState.Fail
, for initiating state. Init value is ‘fail’ because it didn’t started(or you can make state ‘Init’ for this).
When user try to call API to login or check state, it will change state as Loading
until it gets the result(Success/Fail).
JWT inside Nestjs
Nestjs offers various features for web server development as module, and so as authentication. I’m able to check whether user is authenticated or not by checking value inside ‘Bearer Token’ is valid one. It can be checked by parsing header with code, but Nestjs offers smarter way with passport.js
module.
In Nestjs, it offers Guards
class annotation. As you can expect by the name, it makes whether a given request will be handled by the route handler or not, depending on certain conditions (like permissions, roles, ACLs, etc.) present at run-time. This is pretty useful function for authentication logic.
Also, it makes user to integrate Strategy
which is offered from passport
module.
Passport has a rich ecosystem of strategies that implement various authentication mechanisms. While simple in concept, the set of Passport strategies you can choose from is large and presents a lot of variety
First setup scaffold with Nest CLI, install additional modules for this:
$ npm install --save passport passport-jwt @nestjs/passport @nestjs/jwt
$ npm install --save-dev @types/passport-jwt
@nestjs/passport
is extention for Nestjs, which will offer extendable module for authentication.
Before making authentication guards, make API controllers for API defined in flutter app.
|
|
I’ll make api/user/profile
to be blocked if token is not included or invalid, and api/auth/login
to return token for this.
First, for login:
|
|
If login
has been succeed, it will return access_token
which will be used for authentication.
Now make Guards
for this…
[jwt-auth.guard.ts]
|
|
and define Strategy
for JWT:
[jwt-strategy.ts]
|
|
and call with UseGuards
|
|
This will make api/user/profile
protected from unauthorized call…
$ curl http://localhost:3000/api/user/profile
$ # result -> {"statusCode":401,"error":"Unauthorized"}
$ curl http://localhost:3000/api/user/profile -H "Authorization: Bearer <received-auth-token>"
$ # result -> {"userId": "user-id", "username": "user-name"}
You can find full example here.