🌊
ArangoDBのFoxxで認証システムを作る
Authentication
を実際にやってみたので記載します
フォルダ構成
フォルダー パスの一覧
ボリューム シリアル番号は 347F-7D7D です
E:.
│ index.js
│ manifest.json
│
├─scripts
│ setup.js
│
└─util
auth.js
sessions.js
注意するべき点
module.context.collection('コレクション名')
というふうにコレクション名を指定してますが、このように指定することでfoxapp名_コレクション名
となる。
auth.js
'use strict';
// auth用モジュール
const createAuth = require('@arangodb/foxx/auth');
// authの設定
const auth = createAuth();
// 外部利用のためにエクスポート
module.exports = auth;
sessions.js
'use strict';
// セッション利用用モジュールロード
const sessionsMiddleware = require('@arangodb/foxx/sessions');
// セッションに関しての設定
const sessions = sessionsMiddleware({
storage: module.context.collection('sessions'), // ストレージはsessionsドキュメント
transport: 'cookie' // cookie利用
});
// 外部利用のためにエクスポート
module.exports = sessions;
setup.js
'use strict';
// ArangoDB使用するためのライブラリ
const db = require('@arangodb').db;
// usersコレクションがなければ作成する
const usersCN = module.context.collectionName('users');
if (!db._collection(usersCN)) {
db._createDocumentCollection(usersCN);
}
// sessionsコレクションがなければ作成する
const sessions = module.context.collectionName('sessions');
if (!db._collection(sessions)) {
db._createDocumentCollection(sessions);
}
// インデックス作成(usernameをユニークキーに)
module.context.collection('users').ensureIndex({
type: 'hash',
unique: true,
fields: ['username']
});
// ユーザーを作成する(authを利用してパスワード生成)
const auth = require('../util/auth');
const users = module.context.collection('users');
if (!users.firstExample({ username: 'admin' })) {
users.save({
username: 'admin',
password: auth.create('hunter2')
});
}
manifest.json
{
"engines": {
"arangodb": "^3.0.0"
},
"main": "index.js",
"scripts": {
"setup": "scripts/setup.js"
}
}
index.js
'use strict';
// 認証用
const auth = require('./util/auth');
// セッション用
const sessions = require('./util/sessions');
module.context.use(sessions);
// ユーザーコレクション
const users = module.context.collection('users');
// joi 値チェック用ライブラリ
const joi = require('joi');
// foxx用ルータ
const createRouter = require('@arangodb/foxx/router');
const router = createRouter();
module.context.use(router);
// /login の処理
router.post('/login', function(req, res) {
// データ取得(username = req.body.username)
const user = users.firstExample({
username: req.body.username
});
// ユーザーおよびパスワードを確認する
const valid = auth.verify(
user ? user.password : {}, // ユーザーデータが取ってこれなかったときは空っぽにする
req.body.password // 送られてきたパスワード
);
// 確認できなかった?
if (!valid) {
// unauthorizedを返す
res.throw('unauthorized');
}
// ユーザー名は変更される場合があるので_keyをセッションに保持する
req.session.uid = user._key;
// ストレージに保存
req.sessionStorage.save(req.session);
// ユーザー名を返す
res.send({ username: user.username });
})
.body(
// 入力チェック
joi
.object({
username: joi.string().required(),
password: joi.string().required()
})
.required()
);
// /me の処理
router.get('/me', function(req, res) {
try {
// セッションからユーザーを取得
const user = users.document(req.session.uid);
// ユーザー名を返す
res.send({ username: user.username });
} catch (e) {
// not foundを返す
res.throw('not found');
}
});
// /logout の処理
router.post('/logout', function(req, res) {
// セッションにデータが有る?
if (req.session.uid) {
// セッションのクリア
req.session.uid = null;
// ストレージのクリア
req.sessionStorage.save(req.session);
}
// レスポンスをno contentとする(204)
res.status('no content');
});
router.get('/login', function(req, res) {
res.send(`<html>
<head>
<meta charset="utf-8">
<script src="https://unpkg.com/vue@next"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
<div id="basic-event">
<label for="username">Enter your name: </label>
<input type="text" v-model="username" id="username" required>
<label for="password">Enter your password: </label>
<input type="password" v-model="password" id="password" required>
<button @click="login">ログイン</button>
<button @click="me">ME</button>
</div>
</body>
<script>
Vue.createApp({
data() {
return {
username: 'admin',
password: 'hunter2',
}
},
methods: {
login(e) {
axios.post('./login', {
username: this.username,
password: this.password,
})
.then(res => console.log(res.data))
},
me(e) {
axios.get('./me')
.then(res => console.log(res.data))
},
}
}).mount('#basic-event')
</script>
</html>
`
);
})
.response(['text/html'], 'A generic greeting.')
.summary('Generic greeting')
.description('Prints a generic greeting.');
データに関して
authfoxx_sessionsコレクション
_id:authfoxx_sessions/f0f87fb4404b8b72ce56d13c7717987e8a6494c7511a8c04b83bac16750a708f
_rev:_dC7eayy---
_key:f0f87fb4404b8b72ce56d13c7717987e8a6494c7511a8c04b83bac16750a708f
{
"uid": "150448",
"created": 1633419288966,
"expires": 1633422893517,
"data": null
}
このような感じで、_key
の値をcookieのsidに保存している
uid
にはauthfoxx_users
コレクションの各データの_key値が入っている(これでauthfoxx_sessions
とauthfoxx_users
が出来ている)
authfoxx_usersコレクション
_id:authfoxx_users/150448
_rev:_dC3cqgW---
_key:150448
{
"username": "admin",
"password": {
"method": "sha256",
"salt": "c7?Vp&ZY<z7z?Ul{",
"hash": "c8380aca258d174698e099960c0d6227f7003a55f67ca95173b1c5433507c157"
}
}
Discussion