The importance of security and efficient authentication in modern web apps cannot be overly emphasized. From my previous post, Using Facebook/Twitter Authentication in Adonis 4.0. Effective user authentication via Facebook and twitter was discussed and implemented. In this article, we will be talking about authentication using Google, Github and Instagram accounts. An Adonis package Adonis-ally will be employed to achieve this seamlessly.
在现代Web应用程序中,安全性和有效身份验证的重要性不可过分强调。 从我之前的文章“ 在Adonis 4.0中使用Facebook / Twitter身份验证”开始 。 讨论并实现了通过Facebook和Twitter进行的有效用户身份验证。 在本文中,我们将讨论使用Google,Github和Instagram帐户进行身份验证。 我们将采用Adonis软件包Adonis-ally来无缝实现这一目标。
##Adonis-ally The Adonis-ally package is an awesome package that makes it easier to authenticate user via Facebook, Twitter, Google, Linkedin, Instagram, Foursquare and Github. It makes it seamless to implement social authentication and also, more secure.
## Adonis-ally Adonis-ally软件包是一个了不起的软件包,它使通过Facebook,Twitter,Google,Linkedin,Instagram,Foursquare和Github进行身份验证变得更加容易。 它可以无缝地实现社交身份验证,并且更加安全。
##Prerequisites
##先决条件
To get started, you need knowledge of Node.js and JavaScript. The following must be installed on your machine:
首先,您需要了解Node.js和JavaScript。 您的计算机上必须安装以下软件:
##Setup an Adonis Project Open your terminal and type this command.
##设置一个Adonis项目打开您的终端并输入此命令。
# if you don't have Adonis CLI intalled on your machine.
$ npm i -g @adonisjs/cli
# Create a new adonis app and move into the app directory
$ adonis new adonis-social-ii && cd adonis-social-ii
Start the server and test if it's working:
启动服务器并测试其是否正常工作:
$ adonis serve --dev
2017-10-18T09:09:16.649Z - info: serving app on http://127.0.0.1:3333
Open your browser and make a request to http://127.0.0.1:3333. You should see the following.
打开浏览器,并请求http://127.0.0.1:3333 。 您应该看到以下内容。
Let's install adonis-ally
package via adonis CLI
让我们通过adonis CLI安装adonis-ally
软件包
adonisinstall @adonisjs/ally
Register adonis-ally provider to the application inside start/app.js
file.
在start/app.js
文件中向该应用程序注册adonis-ally提供程序。
const providers = [
//...
'@adonisjs/ally/providers/AllyProvider'
//...
]
数据库设置 ( Database Setup )
I will be using MySQL for this tutorial. Create a database called adonis-social
. Get your database's username and password and add it to the .env
file in the project's root directory.
我将在本教程中使用MySQL。 创建一个名为adonis-social
的数据库。 获取数据库的用户名和密码,并将其添加到项目根目录下的.env
文件中。
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=adonis-social
DB_USERNAME=root
DB_PASSWORD=adonisjs
迁移与模型 ( Migrations and Models )
Since Adonis installation comes with pre-installed migration and model files for user, We will modify the existing user model and migration file before we migrate it.
由于Adonis安装随附了针对用户的预安装迁移和模型文件,因此我们将在迁移现有用户模型和迁移文件之前对其进行修改。
Go to "database/migrations" directory, delete token.js file then edit `user.js`
转到“数据库/迁移”目录,删除token.js文件,然后编辑` user.js`
###TIMESTAMP_user.js
### TIMESTAMP_user.js
'use strict'
const Schema = use('Schema')
class UserSchema extends Schema {
up () {
this.create('users', table => {
table.increments()
table.string('name').nullable()
table.string('avatar').nullable()
table.string('username', 80).nullable()
table.string('email', 254).nullable()
table.string('provider_id').nullable()
table.string('provider').nullable()
table.string('password', 60).nullable()
table.timestamps()
})
}
down () {
this.drop('users')
}
}
module.exports = UserSchema
To migrate our table,let's install mysql module
要迁移我们的表,让我们安装mysql模块
$npm install --save mysql
Let's go ahead with the migration.
让我们继续进行迁移。
adonis migration:run
Check your database, users
table is created.
检查您的数据库,创建users
表。
Let's go to app/Models
directory, delete Token.js
file then edit User.js
file
让我们转到app/Models
目录,删除Token.js
文件,然后编辑User.js
文件
'use strict'
const Model = use('Model')
class User extends Model {
static boot () {
super.boot()
/**
* A hook to bash the user password before saving
* it to the database.
*
* Look at `app/Models/Hooks/User.js` file to
* check the hashPassword method
*/
this.addHook('beforeSave', 'User.hashPassword')
}
static get table () {
return 'users'
}
static get primaryKey () {
return 'id'
}
}
module.exports = User
##Obtaining Client id and secret. We are going obtain Google/Github/Instagram client id and secret in this section.
##获取客户端ID和机密。 我们将在本节中获取Google / Github / Instagram客户ID和密码。
谷歌 (Google)
- Visit Google Cloud Console and create new project there 访问Google Cloud Console并在那里创建新项目
- Select APIs and services on the sidebar 在侧边栏中选择API和服务
- Click on Enable APIs and service 点击启用API和服务
- Select Google+ API under social tab and enable it 在社交标签下选择Google+ API并启用它
- Next, Click on Credentials tab by the left 接下来,单击左侧的“凭据”选项卡
- Select OAuth client id from create Credentials dropdown 从“创建凭据”下拉列表中选择OAuth客户端ID
- Select web application, click on configure consent screen 选择Web应用程序,单击“配置同意”屏幕
- Fill out the required fields then click on Save 填写必填字段,然后单击“保存”
- Select web application, Fill out the required fields then click on Save 选择Web应用程序,填写必填字段,然后单击“保存”。
- Name : Adonis login 姓名:Adonis登录
- Authorized Javascript origins: http://localhost:3333 授权JavaScript来源: http:// localhost:3333
- Authorized redirect URI: http://localhost:3333/authenticated/google 授权的重定向URI: http:// localhost:3333 / authenticated / google
- copy the client id and secret into .env 将客户端ID和密码复制到.env中
GOOGLE_CLIENT_ID=xxxxxxxx
GOOGLE_CLIENT_SECRET=xxxxxxxx
###Github
### Github
- Visit Developer Setting 访问开发者设置
- click on New OAuth App 点击新的OAuth应用
- Enter Application Name and Homepage URL 输入应用程序名称和主页URL
- For Authorization callback URL: http://localhost:3333/authenticated/github. 对于授权回调URL: http:// localhost:3333 / authenticated / github 。
- Click Register application 点击注册申请
- Copy the client id and secret into .env 将客户端ID和密码复制到.env中
GITHUB_CLIENT_ID=xxxxxxxx
GITHUB_CLIENT_SECRET=xxxxxxxx
- Visit Manage Clients 访问管理客户
- click on Register a New Client. 单击“ 注册新客户” 。
- Enter Application Name, description, Contact Email and website URL. 输入应用程序名称,描述,联系电子邮件和网站URL。
- For Valid redirect URIs:
http://localhost:3333/authenticated/instagram
对于有效的重定向URI:http://localhost:3333/authenticated/instagram
- Don't forget to complete captcha challenge. 不要忘记完成验证码挑战。
- Copy the client id and secret into .env 将客户端ID和密码复制到.env中
INSTAGRAM_CLIENT_ID=xxxxxx
INSTAGRAM_CLIENT_SECRET=xxxxxx
Now,we have our client id and secret. Let's update config/service.js
file, under ally
object, add the following keys if it does not exist.
现在,我们有我们的客户ID和密码。 让我们更新config/service.js
文件,在ally
对象下,添加以下键(如果不存在)。
[...]
google: {
clientId: Env.get('GOOGLE_CLIENT_ID'),
clientSecret: Env.get('GOOGLE_CLIENT_SECRET'),
redirectUri: `${Env.get('APP_URL')}/authenticated/google`
},
github: {
clientId: Env.get('GITHUB_CLIENT_ID'),
clientSecret: Env.get('GITHUB_CLIENT_SECRET'),
redirectUri: `${Env.get('APP_URL')}/authenticated/github`
},
instagram: {
clientId: Env.get('INSTAGRAM_CLIENT_ID'),
clientSecret: Env.get('INSTAGRAM_CLIENT_SECRET'),
redirectUri: `${Env.get('APP_URL')}/authenticated/instagram`
}
[...]
##Application Views In this section, we will be creating some views for our application. Go to "resource/views" directory then create file called master.edge
##应用程序视图在本节中,我们将为我们的应用程序创建一些视图。 转到“资源/视图”目录,然后创建名为master.edge
文件
###master.edge
### master.edge
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="description" content="AdonisJs Social">
<meta name="author" content="">
<title>AdonisJs Social</title>
<!-- Fonts -->
{{ css('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css') }}
{{ css('https://fonts.googleapis.com/css?family=Lato:100,300,400,700') }}
<!-- Styles -->
{{ css('https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-beta/css/bootstrap.min.css') }}
{{ css('https://cdnjs.cloudflare.com/ajax/libs/bootstrap-social/5.1.1/bootstrap-social.min.css') }}
{{ css('style.css') }}
</head>
<body id="app-layout">
<nav class="navbar navbar-expand-md navbar-dark fixed-top">
<a class="navbar-brand" href="{{ route('welcomePage') }}"><i class="fa fa-cube"></i> Adonis</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarCollapse">
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<a class="nav-link {{ url == route('welcomePage') ? 'active' : '' }}" href="{{ route('welcomePage') }}">HOME</a>
</li>
</ul>
<!-- Right Side Of Navbar -->
<ul class="navbar-nav navbar-right">
<!-- Authentication Links -->
@loggedIn
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<img src="{{ auth.user.toJSON().avatar }}" style="width: 1.9rem; height: 1.9rem; margin-right: 0.5rem" class="rounded-circle">
{{ auth.user.name }} <span class="caret"></span>
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
<a class="dropdown-item" href="{{ route('logout') }}"><i class="fa fa-btn fa-sign-out"></i> Logout</a>
</div>
</li>
@endloggedIn
</ul>
</div>
</nav>
@!section('content')
<!-- JavaScripts -->
{{ script('https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.slim.min.js') }}
{{ script('https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.11.0/umd/popper.min.js') }}
{{ script('https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-beta/js/bootstrap.min.js') }}
</body>
</html>
Also, replace the content of welcome.edge
file
另外,替换welcome.edge
文件的内容
欢迎光临 (welcome.edge)
@layout('master')
@section('content')
<div class="container" style="margin-top: 160px">
<div class="row">
<div class="col-md-1"></div>
<div class="col-md-10">
<div class="card">
@loggedIn
<div class="card-header">User Information</div>
<div class="card-body">
<div class="container">
<div class="row justify-content-md-center">
<div class="col col-md-12">
<img src="{{ auth.user.avatar }}">
<h2>{{ auth.user.name }}</h2>
<h5>{{ auth.user.provider }}</h5>
</div>
</div>
</div>
<br>
</div>
@else
<div class="card-header">Authentication</div>
<div class="card-body">
<div class="container">
<div class="row justify-content-md-center">
<div class="col col-md-6">
<a href="{{ route('social.login', {provider: 'google'}) }}"
class="btn btn-block btn-google btn-social">
<i class="fa fa-google-plus"></i>Login with Google
</a>
<a href="{{ route('social.login', { provider: 'github' }) }}"
class="btn btn-block btn-github btn-social">
<i class="fa fa-github"></i>Login with GitHub
</a>
<a href="{{ route('social.login', {provider: 'instagram'}) }}"
class="btn btn-block btn-instagram btn-social">
<i class="fa fa-instagram"></i>Login with Instagram
</a>
</div>
</div>
</div>
<br>
</div>
@endloggedIn
</div>
</div>
</div>
</div>
@endsection
Refresh your browser, your home page should look like this.
刷新浏览器,您的主页应如下所示。
##Routes
##路线
Time to take care of our application's route. Go to "start/routes.js" and replace the content with:
是时候照顾我们的应用程序的路线了。 转到“ start / routes.js”,并将内容替换为:
###routes.js
### routes.js
'use strict'
const Route = use('Route')
Route.on('/').render('welcome')
Route.get('/logout', 'AuthController.logout').as('logout')
Route.get('/auth/:provider', 'AuthController.redirectToProvider').as('social.login')
Route.get('/authenticated/:provider', 'AuthController.handleProviderCallback').as('social.login.callback')
Three routes was added to the existing ones. One for redirecting the user to the OAuth provider(Google, Github and Instagram in our case), another one for receiving the callback from the provider after authentication and last one for logout.
现有路线中增加了三条路线。 一个用于将用户重定向到OAuth提供程序(在本例中为Google,Github和Instagram),另一个用于在身份验证后接收来自提供程序的回调,最后一个用于注销。
##Controllers Let's create a controller for the application. we are going to call it AuthController
. Run the below command to create this controller.
## Controllers让我们为应用程序创建一个控制器。 我们将其称为AuthController
。 运行以下命令创建此控制器。
adonis make:controller AuthController
It will ask you Generating a controller for ? select Http Request
它将要求您生成控制器用于? 选择Http Request
Go to app/Controllers/Http/
directory, you will discover a controller called AuthController.js
has been created.
转到app/Controllers/Http/
目录,您将发现一个名为AuthController.js
的控制器已创建。
###AuthController.js
### AuthController.js
'use strict'
const User = use('App/Models/User')
class AuthController {
async redirectToProvider ({ally, params}) {
await ally.driver(params.provider).redirect()
}
async handleProviderCallback ({params, ally, auth, response}) {
const provider = params.provider
try {
const userData = await ally.driver(params.provider).getUser()
const authUser = await User.query().where({
'provider': provider,
'provider_id': userData.getId()
}).first()
if (!(authUser === null)) {
await auth.loginViaId(authUser.id)
return response.redirect('/')
}
const user = new User()
user.name = userData.getName()
user.username = userData.getNickname()
user.email = userData.getEmail()
user.provider_id = userData.getId()
user.avatar = userData.getAvatar()
user.provider = provider
await user.save()
await auth.loginViaId(user.id)
return response.redirect('/')
} catch (e) {
console.log(e)
response.redirect('/auth/' + provider)
}
}
async logout ({auth, response}) {
await auth.logout()
response.redirect('/')
}
}
module.exports = AuthController
Before we test our code, let's talk about the functions in the controller.
在测试代码之前,让我们谈谈控制器中的功能。
redirectToProvider
handles redirecting of the user to the OAuth provider(Google, Github and Instagram).redirectToProvider
处理用户到OAuth提供程序(Google,Github和Instagram)的重定向。handleProviderCallback
handles retrieve the user's information from the provider(Google, Github and Instagram). In this method, we checked if the user already exist in the database. If so,we return the user's information. Otherwise, create a new user. This concept prevent user account to be created twice.Note: when you test the application with google chrome,it might not log you in because of many re-direct that occurs in the background.To fix this, you have to set
sameSite: false
insideconfig/session.js
. But for other browsers, you should be fine.handleProviderCallback
句柄从提供者(Google,Github和Instagram)检索用户的信息。 在这种方法中,我们检查用户是否已存在于数据库中。 如果是这样,我们将返回用户的信息。 否则,请创建一个新用户。 此概念可防止两次创建用户帐户。注意:当您使用google chrome测试应用程序时,由于在后台进行许多
sameSite: false
,它可能无法登录。要解决此问题,必须在config/session.js
设置sameSite: false
。 但是对于其他浏览器,您应该没问题。
The view when i login via google, github and instagram.
我通过Google,github和instagram登录时的视图。
Github
结论 ( Conclusion )
Your application now provide users the option of using different social login providers to securely gain access to your service. No restrictions. No boundaries.
现在,您的应用程序为用户提供了使用其他社交登录提供程序的选项,以安全地访问您的服务。 无限制。 没有界限。
If you have any questions or observations, feel free to drop it in the comments section below. I would be happy to respond to you.
如果您有任何疑问或意见,请随时将其放在下面的评论部分。 我很高兴回应您。