Skip to content

Instantly share code, notes, and snippets.

@tatsuyasusukida
Last active May 3, 2022 01:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tatsuyasusukida/0a5efa57daa27cb9ab04bab01f8b789f to your computer and use it in GitHub Desktop.
Save tatsuyasusukida/0a5efa57daa27cb9ab04bab01f8b789f to your computer and use it in GitHub Desktop.
😀 Node.jsでBasic認証を設定する方法【動画版あり】

😀 Node.jsでBasic認証を設定する方法

はじめに

この記事ではNode.jsでBasic認証を行う方法について説明します。説明にあたり、下記4つのケースを想定します。

  • Expressを使い、npmパッケージを使うケース
  • Expressを使い、npmパッケージを使わないケース
  • Expressを使わず、npmパッケージを使うケース
  • Expressを使わず、npmパッケージを使わないケース

コーディングの準備

ターミナルで下記のコマンドを実行してコーディングの準備をします。

mkdir nodejs-basic-auth
cd nodejs-basic-auth
npm init -y
npm install --save express basic-auth-connect basic-auth
touch case-01.js case-02.js case-03.js case-04.js

ケース1:Expressを使い、npmパッケージを使うケース

case-01.jsのソースコードを下記に示します。

クリックしてcase-01.jsへ移動

上記のソースコードでは basic-auth-connect を利用していますが、npmのページに書かれているようにこのnpmパッケージは非推奨扱いなので basic-auth などを使ってミドルウェアを自作することが推奨されています。代替するnpmパッケージとしては express-basic-auth などが候補として挙げられます。

動作確認の方法

ターミナルで下記のコマンドを実行してWebサーバーを起動します。

node case-01.js

続いて新規のターミナルを起動してから下記のコマンドを実行して認証情報なしではアクセスできないことを確認します。

curl -v http://localhost:3000/

上記のコマンドの実行結果を下記に示します。

*   Trying ::1:3000...
* Connected to localhost (::1) port 3000 (#0)
> GET / HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.77.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 401 Unauthorized
< X-Powered-By: Express
< WWW-Authenticate: Basic realm="Authorization Required"
< Date: Mon, 02 May 2022 02:15:20 GMT
< Connection: keep-alive
< Keep-Alive: timeout=5
< Content-Length: 12
< 
* Connection #0 to host localhost left intact
Unauthorized

続いて下記のコマンドを実行して認証情報ありでアクセスできることを確認します。

curl -v http://username:password@localhost:3000/

上記のコマンドの実行結果を下記に示します。

*   Trying ::1:3000...
* Connected to localhost (::1) port 3000 (#0)
* Server auth using Basic with user 'username'
> GET / HTTP/1.1
> Host: localhost:3000
> Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
> User-Agent: curl/7.77.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< X-Powered-By: Express
< Content-Type: application/json; charset=utf-8
< Content-Length: 11
< ETag: W/"b-Ai2R8hgEarLmHKwesT1qcY913ys"
< Date: Mon, 02 May 2022 02:15:55 GMT
< Connection: keep-alive
< Keep-Alive: timeout=5
< 
* Connection #0 to host localhost left intact
{"ok":true}

ケース2:Expressを使い、npmパッケージを使わないケース

case-02.jsのソースコードを下記に示します。

クリックしてcase-02.jsへ移動

ポイントを下記に示します。

  1. Authorizationヘッダの有無を確認しています。
  2. Authorizationヘッダを解析しています。
  3. 認証方式が"Basic"であることを確認しています。
  4. ユーザー名とパスワードを確認しています。
  5. 認証が成功した場合は次のミドルウェアへ進みます。
  6. 認証が失敗した場合は401(Unauthorized)応答を返します。

動作確認の方法についてはケース1と同様ですので割愛します。

ケース3:Expressを使わず、npmパッケージを使うケース

case-03.jsのソースコードを下記に示します。

クリックしてcase-03.jsへ移動

上記のソースコードでは basic-auth を利用しています。 このnpmパッケージを利用することで下記3点の処理を自分で実装する必要が無くなります。

  • Authorizationヘッダの有無の確認
  • Authorizationヘッダの解析
  • 認証方式が"Basic"であることの確認

動作確認の方法についてはケース1と同様ですので割愛します。

ケース4:Expressを使わず、npmパッケージを使わないケース

case-04.jsのソースコードを下記に示します。

クリックしてcase-04.jsへ移動

内容はケース3とケース4と合わせ技です。動作確認の方法についてはケース1と同様ですので割愛します。

おわりに

どの方法でもBasic認証を行うことができますが、Expressを利用する場合は basic-auth-connectexpress-basic-auth を使い、Expressを利用しない場合は basic-auth を使うケースが多いのではないかと思います。

私はExpressを利用しており普段は basic-auth-connect を使っていますが、記事を書くために色々と調べて今後は express-basic-auth を使おうと思いました。

いまだに何かと必要となる場面の多いBasic認証ですが、色々な方法がある中でどの方法を選べば良いかを決めるための参考になれば幸いです。また、お気づきの点などありましたらお気軽にコメントをいただければ幸いです。最後までお読みいただきありがとうございました!

/node_modules/
/package-lock.json
# Do not ignore package-lock.json other than gist.
const express = require('express')
const basicAuth = require('basic-auth-connect')
if (require.main === module) {
main()
}
async function main () {
try {
const router = express()
router.use(basicAuth('username', 'password'))
router.get('/', (req, res) => {
res.send({ok: true})
})
const port = parseInt(process.env.PORT || '3000', 10)
router.listen(port, () => {
console.info(`Listening on ${port}`)
})
} catch (err) {
console.error(err)
}
}
const express = require('express')
if (require.main === module) {
main()
}
async function main () {
try {
const router = express()
router.use((req, res, next) => {
if (req.headers['authorization']) { // <1>
const authorization = req.headers['authorization']
const pieces = authorization.split(/\s+/g) // <2>
const [type] = pieces
if (type === 'Basic') { // <3>
const buffer = Buffer.from(pieces[1], 'base64')
const credentials = buffer.toString()
const [username, password] = credentials.split(':')
if (username === 'username' && password === 'password') { // <4>
next() // <5>
return
}
}
}
res.set('WWW-Authenticate', 'Basic realm="realm"') // <6>
res.status(401).end()
})
router.get('/', (req, res) => {
res.send({ok: true})
})
const port = parseInt(process.env.PORT || '3000', 10)
router.listen(port, () => {
console.info(`Listening on ${port}`)
})
} catch (err) {
console.error(err)
}
}
const http = require('http')
const auth = require('basic-auth')
if (require.main === module) {
main()
}
async function main () {
try {
const server = http.createServer()
server.on('request', (req, res) => {
const credentials = auth(req)
if (credentials) {
const {name, pass} = credentials
if (name === 'username' && pass === 'password') {
const content = JSON.stringify({ok: true})
res.writeHead(200, {
'Content-Type': 'application/json; charset=utf-8',
'Content-Length': `${content.length}`,
})
res.write(content)
res.end()
return
}
}
res.writeHead(401, {
'WWW-Authenticate': 'Basic realm="realm"',
'Content-Length': '0',
})
res.end()
})
const port = parseInt(process.env.PORT || '3000', 10)
server.listen(port, () => {
console.info(`Listening on ${port}`)
})
} catch (err) {
console.error(err)
}
}
const http = require('http')
if (require.main === module) {
main()
}
async function main () {
try {
const server = http.createServer()
server.on('request', (req, res) => {
if (req.headers['authorization']) {
const authorization = req.headers['authorization']
const pieces = authorization.split(/\s+/g)
const [type] = pieces
if (type === 'Basic') {
const buffer = Buffer.from(pieces[1], 'base64')
const credentials = buffer.toString()
const [username, password] = credentials.split(':')
if (username === 'username' && password === 'password') {
const content = JSON.stringify({ok: true})
res.writeHead(200, {
'Content-Type': 'application/json; charset=utf-8',
'Content-Length': `${content.length}`,
})
res.write(content)
res.end()
return
}
}
}
res.writeHead(401, {
'WWW-Authenticate': 'Basic realm="realm"',
'Content-Length': '0',
})
res.end()
})
const port = parseInt(process.env.PORT || '3000', 10)
server.listen(port, () => {
console.info(`Listening on ${port}`)
})
} catch (err) {
console.error(err)
}
}
{
"name": "nodejs-basic-auth",
"version": "1.0.0",
"description": "",
"main": "case-01.js",
"scripts": {
"test": "curl -v http://username:password@localhost:3000/"
},
"keywords": [],
"author": "",
"license": "MIT",
"dependencies": {
"basic-auth": "^2.0.1",
"basic-auth-connect": "^1.0.0",
"express": "^4.18.1"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment