Added 2FA with U2F keys.

This commit is contained in:
Jan Böhmer 2019-12-29 13:35:30 +01:00
parent 47fa8b04e5
commit 069293a843
18 changed files with 512 additions and 157 deletions

134
assets/js/u2f_auth.js Normal file
View file

@ -0,0 +1,134 @@
import u2fApi from 'u2f-api'
'use strict'
window.u2fauth = window.u2fauth || {}
u2fauth.formId = 'u2fForm'
u2fauth.authCodeId = '_auth_code'
u2fauth.keynameId = 'u2fkeyname'
u2fauth.pressButtonId = 'u2fpressbutton'
u2fauth.errorId = 'u2fError'
u2fauth.timeout = 30
u2fauth.errorTranslation = {
1: 'Unknown Error',
2: 'Bad Request',
3: 'Client configuration not supported',
4: 'Device already registered or ineligible',
5: 'Timeout. Click to retry'
}
u2fauth.ready = function (fn) {
if (document.readyState !== 'loading') {
fn()
} else if (document.addEventListener) {
document.addEventListener('DOMContentLoaded', fn)
} else {
document.attachEvent('onreadystatechange', function () {
if (document.readyState !== 'loading') { fn() }
})
}
}
u2fauth.authenticate = function () {
u2fauth.clearError()
u2fauth.showPressButton()
var form = document.getElementById(u2fauth.formId)
var request = JSON.parse(form.dataset.request)
u2fApi.isSupported()
.then(function (supported) {
if (supported) {
return u2fApi.sign(request, u2fauth.timeout)
.then(response => {
u2fauth.hidePressButton()
u2fauth.submit(form, response)
})
} else {
alert('Browser not supported')
}
})
.catch(data => {
u2fauth.hidePressButton()
u2fauth.showError(data.metaData.code, u2fauth.authenticate)
})
}
u2fauth.register = function () {
u2fauth.clearError()
u2fauth.hideKeyname()
u2fauth.showPressButton()
var form = document.getElementById(u2fauth.formId)
var request = JSON.parse(form.dataset.request)
u2fApi.isSupported()
.then(function (supported) {
if (supported) {
return u2fApi.register(request[0], request[1], u2fauth.timeout)
.then(response => {
u2fauth.hidePressButton()
u2fauth.submit(form, response)
})
} else {
alert('Browser not supported')
}
})
.catch(data => {
console.info(data)
u2fauth.hidePressButton()
u2fauth.showError(data.metaData.code, u2fauth.register)
})
}
u2fauth.submit = function (form, data) {
var codeField = document.getElementById(u2fauth.authCodeId)
codeField.value = JSON.stringify(data)
form.submit()
}
u2fauth.hideKeyname = function () {
var keyname = document.getElementById(u2fauth.keynameId)
keyname.style.display = 'none'
}
u2fauth.hidePressButton = function () {
var pressButton = document.getElementById(u2fauth.pressButtonId)
pressButton.style.display = 'none'
}
u2fauth.showPressButton = function () {
var pressButton = document.getElementById(u2fauth.pressButtonId)
pressButton.style.display = 'block'
}
u2fauth.clearError = function () {
var errorDisplay = document.getElementById(u2fauth.errorId)
errorDisplay.style.display = 'none'
errorDisplay.innerText = ''
}
u2fauth.showError = function (error, callback) {
var errorDisplay = document.getElementById(u2fauth.errorId)
errorDisplay.style.display = 'block'
errorDisplay.innerText = u2fauth.errorTranslation[error]
errorDisplay.onclick = callback
}
u2fauth.ready(function () {
const form = document.getElementById('u2fForm')
if (!form) {
return
}
const type = form.dataset.action
if (type === 'auth') {
u2fauth.authenticate()
} else if (type === 'reg' && form.addEventListener) {
form.addEventListener('submit', function (event) {
event.preventDefault()
u2fauth.register()
}, false)
}
})

View file

@ -495,6 +495,29 @@ $(document).on("ajaxUI:start ajaxUI:reload", function() {
});
});
//Register U2F on page reload too...
$(document).on("ajaxUI:reload", function() {
//@ts-ignore
window.u2fauth.ready(function () {
const form = document.getElementById('u2fForm')
if (!form) {
return
}
const type = form.dataset.action
if (type === 'auth') {
//@ts-ignore
u2fauth.authenticate()
} else if (type === 'reg' && form.addEventListener) {
form.addEventListener('submit', function (event) {
event.preventDefault()
//@ts-ignore
u2fauth.register()
}, false)
}
})
});
//Need for proper body padding, with every navbar height
$(window).resize(function () {
let height : number = $('#navbar').height() + 10;

View file

@ -20,7 +20,7 @@
"nyholm/psr7": "^1.1",
"ocramius/proxy-manager": "2.1.*",
"omines/datatables-bundle": "^0.3.1",
"r/u2f-two-factor-bundle": "^0.7.0",
"r/u2f-two-factor-bundle": "dev-u2f-api",
"php-translation/symfony-bundle": "^0.9.1",
"s9e/text-formatter": "^2.1",
"scheb/two-factor-bundle": "^4.11",
@ -115,5 +115,11 @@
"allow-contrib": false,
"require": "4.4.*"
}
}
},
"repositories": [
{
"type": "vcs",
"url": "git@github.com:stephanvierkant/u2f-two-factor-bundle.git"
}
]
}

187
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "d47b430dcf83d23e3fb046457877da76",
"content-hash": "41b73aa723ad9c4a6f64b1fb0f7b6c5f",
"packages": [
{
"name": "beberlei/assert",
@ -1818,16 +1818,16 @@
},
{
"name": "league/html-to-markdown",
"version": "4.9.0",
"version": "4.9.1",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/html-to-markdown.git",
"reference": "71319108e3db506250b8987721b13568fd9fa446"
"reference": "1dcd0f85de786f46a7f224a27cc3d709ddd2a68c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/html-to-markdown/zipball/71319108e3db506250b8987721b13568fd9fa446",
"reference": "71319108e3db506250b8987721b13568fd9fa446",
"url": "https://api.github.com/repos/thephpleague/html-to-markdown/zipball/1dcd0f85de786f46a7f224a27cc3d709ddd2a68c",
"reference": "1dcd0f85de786f46a7f224a27cc3d709ddd2a68c",
"shasum": ""
},
"require": {
@ -1878,7 +1878,7 @@
"html",
"markdown"
],
"time": "2019-11-02T14:54:14+00:00"
"time": "2019-12-28T01:32:28+00:00"
},
{
"name": "liip/imagine-bundle",
@ -2589,16 +2589,16 @@
},
{
"name": "php-http/discovery",
"version": "1.7.0",
"version": "1.7.3",
"source": {
"type": "git",
"url": "https://github.com/php-http/discovery.git",
"reference": "e822f86a6983790aa17ab13aa7e69631e86806b6"
"reference": "2e0ad94833ec8473280454590a9011a0cd2dfc56"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-http/discovery/zipball/e822f86a6983790aa17ab13aa7e69631e86806b6",
"reference": "e822f86a6983790aa17ab13aa7e69631e86806b6",
"url": "https://api.github.com/repos/php-http/discovery/zipball/2e0ad94833ec8473280454590a9011a0cd2dfc56",
"reference": "2e0ad94833ec8473280454590a9011a0cd2dfc56",
"shasum": ""
},
"require": {
@ -2650,20 +2650,20 @@
"message",
"psr7"
],
"time": "2019-06-30T09:04:27+00:00"
"time": "2019-12-27T19:51:10+00:00"
},
{
"name": "php-http/httplug",
"version": "v2.0.0",
"version": "2.1.0",
"source": {
"type": "git",
"url": "https://github.com/php-http/httplug.git",
"reference": "b3842537338c949f2469557ef4ad4bdc47b58603"
"reference": "72d2b129a48f0490d55b7f89be0d6aa0597ffb06"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-http/httplug/zipball/b3842537338c949f2469557ef4ad4bdc47b58603",
"reference": "b3842537338c949f2469557ef4ad4bdc47b58603",
"url": "https://api.github.com/repos/php-http/httplug/zipball/72d2b129a48f0490d55b7f89be0d6aa0597ffb06",
"reference": "72d2b129a48f0490d55b7f89be0d6aa0597ffb06",
"shasum": ""
},
"require": {
@ -2673,13 +2673,13 @@
"psr/http-message": "^1.0"
},
"require-dev": {
"henrikbjorn/phpspec-code-coverage": "^1.0",
"phpspec/phpspec": "^2.4"
"friends-of-phpspec/phpspec-code-coverage": "^4.1",
"phpspec/phpspec": "^4.3.4|^5.0|^6.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0.x-dev"
"dev-master": "2.x-dev"
}
},
"autoload": {
@ -2707,7 +2707,7 @@
"client",
"http"
],
"time": "2018-10-31T09:14:44+00:00"
"time": "2019-12-27T10:07:11+00:00"
},
{
"name": "php-http/message-factory",
@ -3563,16 +3563,16 @@
},
{
"name": "r/u2f-two-factor-bundle",
"version": "0.7.0",
"version": "dev-u2f-api",
"source": {
"type": "git",
"url": "https://github.com/darookee/u2f-two-factor-bundle.git",
"reference": "dcf391e694a8f237883b4c39cfe7367c344c1556"
"url": "https://github.com/stephanvierkant/u2f-two-factor-bundle.git",
"reference": "81212afd7897911eb6bbf3f8b315ae336cb3e45b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/darookee/u2f-two-factor-bundle/zipball/dcf391e694a8f237883b4c39cfe7367c344c1556",
"reference": "dcf391e694a8f237883b4c39cfe7367c344c1556",
"url": "https://api.github.com/repos/stephanvierkant/u2f-two-factor-bundle/zipball/81212afd7897911eb6bbf3f8b315ae336cb3e45b",
"reference": "81212afd7897911eb6bbf3f8b315ae336cb3e45b",
"shasum": ""
},
"require": {
@ -3598,7 +3598,6 @@
"R\\U2FTwoFactorBundle\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -3615,14 +3614,17 @@
"description": "Use U2F-Keys as 2FA for Symfony2, using scheb/two-factor-bundle",
"homepage": "https://github.com/darookee/u2f-two-factor-bundle",
"keywords": [
"Authentication",
"Symfony2",
"authentication",
"fido",
"symfony2",
"two-factor",
"two-step",
"yubikey"
],
"time": "2019-06-05T14:42:26+00:00"
"support": {
"source": "https://github.com/stephanvierkant/u2f-two-factor-bundle/tree/u2f-api"
},
"time": "2019-07-17T10:00:04+00:00"
},
{
"name": "s9e/regexp-builder",
@ -3664,16 +3666,16 @@
},
{
"name": "s9e/text-formatter",
"version": "2.3.0",
"version": "2.3.1",
"source": {
"type": "git",
"url": "https://github.com/s9e/TextFormatter.git",
"reference": "26d6ee3a931a25acfea3096f62f0cc42172f3859"
"reference": "65a0605f163b8ffcf7145357f167b153f31cd168"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/s9e/TextFormatter/zipball/26d6ee3a931a25acfea3096f62f0cc42172f3859",
"reference": "26d6ee3a931a25acfea3096f62f0cc42172f3859",
"url": "https://api.github.com/repos/s9e/TextFormatter/zipball/65a0605f163b8ffcf7145357f167b153f31cd168",
"reference": "65a0605f163b8ffcf7145357f167b153f31cd168",
"shasum": ""
},
"require": {
@ -3698,7 +3700,7 @@
},
"type": "library",
"extra": {
"version": "2.3.0"
"version": "2.3.1"
},
"autoload": {
"psr-4": {
@ -3728,7 +3730,7 @@
"parser",
"shortcodes"
],
"time": "2019-11-17T16:03:56+00:00"
"time": "2019-12-26T19:14:01+00:00"
},
{
"name": "scheb/two-factor-bundle",
@ -3800,16 +3802,16 @@
},
{
"name": "sensio/framework-extra-bundle",
"version": "v5.5.2",
"version": "v5.5.3",
"source": {
"type": "git",
"url": "https://github.com/sensiolabs/SensioFrameworkExtraBundle.git",
"reference": "92acfcc610e2180c52790ec3ff2e893f67e76b32"
"reference": "98f0807137b13d0acfdf3c255a731516e97015de"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sensiolabs/SensioFrameworkExtraBundle/zipball/92acfcc610e2180c52790ec3ff2e893f67e76b32",
"reference": "92acfcc610e2180c52790ec3ff2e893f67e76b32",
"url": "https://api.github.com/repos/sensiolabs/SensioFrameworkExtraBundle/zipball/98f0807137b13d0acfdf3c255a731516e97015de",
"reference": "98f0807137b13d0acfdf3c255a731516e97015de",
"shasum": ""
},
"require": {
@ -3874,7 +3876,7 @@
"annotations",
"controllers"
],
"time": "2019-12-12T16:21:49+00:00"
"time": "2019-12-27T08:57:19+00:00"
},
{
"name": "sensiolabs/security-checker",
@ -8017,16 +8019,16 @@
},
{
"name": "twig/cssinliner-extra",
"version": "v3.0.0",
"version": "v3.0.1",
"source": {
"type": "git",
"url": "https://github.com/twigphp/cssinliner-extra.git",
"reference": "431402520025e077268de4978a4206e5fb8c0103"
"reference": "d7c951e935d65c0cfd9a63bb08541a297f230f3a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/cssinliner-extra/zipball/431402520025e077268de4978a4206e5fb8c0103",
"reference": "431402520025e077268de4978a4206e5fb8c0103",
"url": "https://api.github.com/repos/twigphp/cssinliner-extra/zipball/d7c951e935d65c0cfd9a63bb08541a297f230f3a",
"reference": "d7c951e935d65c0cfd9a63bb08541a297f230f3a",
"shasum": ""
},
"require": {
@ -8035,7 +8037,7 @@
"twig/twig": "^2.4|^3.0"
},
"require-dev": {
"symfony/phpunit-bridge": "^4.4@dev"
"symfony/phpunit-bridge": "^4.4|^5.0"
},
"type": "library",
"extra": {
@ -8067,20 +8069,20 @@
"inlining",
"twig"
],
"time": "2019-10-17T07:27:07+00:00"
"time": "2019-12-27T07:33:44+00:00"
},
{
"name": "twig/extra-bundle",
"version": "v3.0.0",
"version": "v3.0.1",
"source": {
"type": "git",
"url": "https://github.com/twigphp/twig-extra-bundle.git",
"reference": "c56821429490e351003a09b7ed0c917feec2355f"
"reference": "ce5c97dd566d9acd5d1fbd5eb76b6d264614725a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/twig-extra-bundle/zipball/c56821429490e351003a09b7ed0c917feec2355f",
"reference": "c56821429490e351003a09b7ed0c917feec2355f",
"url": "https://api.github.com/repos/twigphp/twig-extra-bundle/zipball/ce5c97dd566d9acd5d1fbd5eb76b6d264614725a",
"reference": "ce5c97dd566d9acd5d1fbd5eb76b6d264614725a",
"shasum": ""
},
"require": {
@ -8090,11 +8092,11 @@
"twig/twig": "^2.4|^3.0"
},
"require-dev": {
"twig/cssinliner-extra": "^2.12|^3.0@dev",
"twig/html-extra": "^2.12@dev|^3.0@dev",
"twig/inky-extra": "^2.12@dev|^3.0@dev",
"twig/intl-extra": "^2.12@dev|^3.0@dev",
"twig/markdown-extra": "^2.12@dev|^3.0@dev"
"twig/cssinliner-extra": "^2.12|^3.0",
"twig/html-extra": "^2.12|^3.0",
"twig/inky-extra": "^2.12|^3.0",
"twig/intl-extra": "^2.12|^3.0",
"twig/markdown-extra": "^2.12|^3.0"
},
"type": "symfony-bundle",
"extra": {
@ -8126,20 +8128,20 @@
"extra",
"twig"
],
"time": "2019-10-17T07:30:08+00:00"
"time": "2019-12-28T07:09:27+00:00"
},
{
"name": "twig/inky-extra",
"version": "v3.0.0",
"version": "v3.0.1",
"source": {
"type": "git",
"url": "https://github.com/twigphp/inky-extra.git",
"reference": "7e33cb931f29e8cbc1f68eafa30e0ca7f7c6ad3b"
"reference": "68d7e3a00cb66dab07093c0c88059f4e02d71b39"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/inky-extra/zipball/7e33cb931f29e8cbc1f68eafa30e0ca7f7c6ad3b",
"reference": "7e33cb931f29e8cbc1f68eafa30e0ca7f7c6ad3b",
"url": "https://api.github.com/repos/twigphp/inky-extra/zipball/68d7e3a00cb66dab07093c0c88059f4e02d71b39",
"reference": "68d7e3a00cb66dab07093c0c88059f4e02d71b39",
"shasum": ""
},
"require": {
@ -8148,7 +8150,7 @@
"twig/twig": "^2.4|^3.0"
},
"require-dev": {
"symfony/phpunit-bridge": "^4.4@dev"
"symfony/phpunit-bridge": "^4.4|^5.0"
},
"type": "library",
"extra": {
@ -8181,20 +8183,20 @@
"inky",
"twig"
],
"time": "2019-10-17T07:27:12+00:00"
"time": "2019-12-27T07:33:44+00:00"
},
{
"name": "twig/intl-extra",
"version": "v3.0.0",
"version": "v3.0.1",
"source": {
"type": "git",
"url": "https://github.com/twigphp/intl-extra.git",
"reference": "642552fa2834d9f56a727645f63e73d59672fb52"
"reference": "291d79ef98891da3efe14f0771fbe03a25fe6bec"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/intl-extra/zipball/642552fa2834d9f56a727645f63e73d59672fb52",
"reference": "642552fa2834d9f56a727645f63e73d59672fb52",
"url": "https://api.github.com/repos/twigphp/intl-extra/zipball/291d79ef98891da3efe14f0771fbe03a25fe6bec",
"reference": "291d79ef98891da3efe14f0771fbe03a25fe6bec",
"shasum": ""
},
"require": {
@ -8203,7 +8205,7 @@
"twig/twig": "^2.4|^3.0"
},
"require-dev": {
"symfony/phpunit-bridge": "^4.4@dev"
"symfony/phpunit-bridge": "^4.4|^5.0"
},
"type": "library",
"extra": {
@ -8234,20 +8236,20 @@
"intl",
"twig"
],
"time": "2019-11-15T20:33:33+00:00"
"time": "2019-12-28T07:09:27+00:00"
},
{
"name": "twig/markdown-extra",
"version": "v3.0.0",
"version": "v3.0.1",
"source": {
"type": "git",
"url": "https://github.com/twigphp/markdown-extra.git",
"reference": "609989e5bdc8cdb282e37fdc54e3591c244b5501"
"reference": "e3f6eb3f65eb2c165451c8417d918fb96fbd6d4d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/markdown-extra/zipball/609989e5bdc8cdb282e37fdc54e3591c244b5501",
"reference": "609989e5bdc8cdb282e37fdc54e3591c244b5501",
"url": "https://api.github.com/repos/twigphp/markdown-extra/zipball/e3f6eb3f65eb2c165451c8417d918fb96fbd6d4d",
"reference": "e3f6eb3f65eb2c165451c8417d918fb96fbd6d4d",
"shasum": ""
},
"require": {
@ -8259,7 +8261,7 @@
"league/commonmark": "^1.0",
"league/html-to-markdown": "^4.8",
"michelf/php-markdown": "^1.8",
"symfony/phpunit-bridge": "^4.4@dev"
"symfony/phpunit-bridge": "^4.4|^5.0"
},
"type": "library",
"extra": {
@ -8291,20 +8293,20 @@
"markdown",
"twig"
],
"time": "2019-10-17T07:30:08+00:00"
"time": "2019-12-28T07:09:27+00:00"
},
{
"name": "twig/twig",
"version": "v2.12.2",
"version": "v2.12.3",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "d761fd1f1c6b867ae09a7d8119a6d95d06dc44ed"
"reference": "97b6311585cae66a26833b14b33785f5797f7d39"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/d761fd1f1c6b867ae09a7d8119a6d95d06dc44ed",
"reference": "d761fd1f1c6b867ae09a7d8119a6d95d06dc44ed",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/97b6311585cae66a26833b14b33785f5797f7d39",
"reference": "97b6311585cae66a26833b14b33785f5797f7d39",
"shasum": ""
},
"require": {
@ -8314,8 +8316,7 @@
},
"require-dev": {
"psr/container": "^1.0",
"symfony/debug": "^3.4|^4.2",
"symfony/phpunit-bridge": "^4.4@dev|^5.0"
"symfony/phpunit-bridge": "^4.4|^5.0"
},
"type": "library",
"extra": {
@ -8344,7 +8345,6 @@
},
{
"name": "Twig Team",
"homepage": "https://twig.symfony.com/contributors",
"role": "Contributors"
},
{
@ -8358,7 +8358,7 @@
"keywords": [
"templating"
],
"time": "2019-11-11T16:52:09+00:00"
"time": "2019-12-28T07:12:03+00:00"
},
{
"name": "webmozart/assert",
@ -9165,12 +9165,12 @@
"source": {
"type": "git",
"url": "https://github.com/Roave/SecurityAdvisories.git",
"reference": "181dd66fd666ed397991ded7b725fe90a8a00413"
"reference": "44a677c8e06241a66409ae6e4820dc166fc09ab2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/181dd66fd666ed397991ded7b725fe90a8a00413",
"reference": "181dd66fd666ed397991ded7b725fe90a8a00413",
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/44a677c8e06241a66409ae6e4820dc166fc09ab2",
"reference": "44a677c8e06241a66409ae6e4820dc166fc09ab2",
"shasum": ""
},
"conflict": {
@ -9261,7 +9261,7 @@
"pusher/pusher-php-server": "<2.2.1",
"robrichards/xmlseclibs": ">=1,<3.0.4",
"sabre/dav": ">=1.6,<1.6.99|>=1.7,<1.7.11|>=1.8,<1.8.9",
"scheb/two-factor-bundle": "<3.26|>=4,<4.11",
"scheb/two-factor-bundle": ">=0,<3.26|>=4,<4.11",
"sensiolabs/connect": "<4.2.3",
"serluck/phpwhois": "<=4.2.6",
"shopware/shopware": "<5.3.7",
@ -9376,7 +9376,7 @@
}
],
"description": "Prevents installation of composer packages with known security vulnerabilities: no API, simply require it",
"time": "2019-12-21T14:43:42+00:00"
"time": "2019-12-26T14:16:40+00:00"
},
{
"name": "sebastian/diff",
@ -9905,22 +9905,23 @@
},
{
"name": "vimeo/psalm",
"version": "3.7.2",
"version": "3.8.0",
"source": {
"type": "git",
"url": "https://github.com/vimeo/psalm.git",
"reference": "d9cae720c1af31db9ba27c2bc1fcf9b0dd092fb0"
"reference": "bdb2f3c2be7bd56b180535dca33d72b74ffe1af7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/vimeo/psalm/zipball/d9cae720c1af31db9ba27c2bc1fcf9b0dd092fb0",
"reference": "d9cae720c1af31db9ba27c2bc1fcf9b0dd092fb0",
"url": "https://api.github.com/repos/vimeo/psalm/zipball/bdb2f3c2be7bd56b180535dca33d72b74ffe1af7",
"reference": "bdb2f3c2be7bd56b180535dca33d72b74ffe1af7",
"shasum": ""
},
"require": {
"amphp/amp": "^2.1",
"amphp/byte-stream": "^1.5",
"composer/xdebug-handler": "^1.1",
"ext-dom": "*",
"felixfbecker/advanced-json-rpc": "^3.0.3",
"felixfbecker/language-server-protocol": "^1.4",
"netresearch/jsonmapper": "^1.0",
@ -9971,7 +9972,8 @@
"Psalm\\": "src/Psalm"
},
"files": [
"src/functions.php"
"src/functions.php",
"src/spl_object_id.php"
]
},
"notification-url": "https://packagist.org/downloads/",
@ -9989,7 +9991,7 @@
"inspection",
"php"
],
"time": "2019-12-03T13:33:31+00:00"
"time": "2019-12-27T22:10:16+00:00"
},
{
"name": "webmozart/glob",
@ -10089,6 +10091,7 @@
"minimum-stability": "stable",
"stability-flags": {
"gregwar/captcha-bundle": 20,
"r/u2f-two-factor-bundle": 20,
"roave/security-advisories": 20
},
"prefer-stable": false,

View file

@ -0,0 +1,4 @@
ru2_f_two_factor:
formTemplate: "/security/U2F/u2f_login.html.twig"
registerTemplate: "/security/U2F/u2f_register.html.twig"
authCodeParameter: _auth_code

View file

@ -50,4 +50,7 @@ security:
# This makes the logout route available during two-factor authentication, allows the user to cancel
- { path: ^/logout, role: IS_AUTHENTICATED_ANONYMOUSLY }
# This ensures that the form can only be accessed when two-factor authentication is in progress
- { path: ^/2fa, role: IS_AUTHENTICATED_2FA_IN_PROGRESS }
- { path: "^/\\w{2}/2fa", role: IS_AUTHENTICATED_2FA_IN_PROGRESS }
# We get into trouble with the U2F authentication, if the calls to the trees trigger an 2FA login
# This settings should not do much harm, because a read only access to show available data structures is not really critical
- { path: "^/\\w{2}/tree", role: IS_AUTHENTICATED_ANONYMOUSLY }

View file

@ -5,3 +5,7 @@
2fa_login_check:
path: /{_locale}/2fa_check
r_u2f_register:
resource: "@RU2FTwoFactorBundle/Resources/config/routing.yml"
prefix: /{_locale}/user

View file

@ -46,6 +46,7 @@
"pdfmake": "^0.1.53",
"qrcode": "^1.4.4",
"ts-loader": "^5.3.3",
"typescript": "^3.3.4000"
"typescript": "^3.3.4000",
"u2f-api": "^1.1.1"
}
}

View file

@ -24,6 +24,7 @@ namespace App\Entity\UserSystem;
use App\Entity\Base\TimestampTrait;
use Doctrine\ORM\Mapping as ORM;
use R\U2FTwoFactorBundle\Model\U2F\TwoFactorInterface;
use R\U2FTwoFactorBundle\Model\U2F\TwoFactorKeyInterface;
use u2flib_server\Registration;
@ -49,25 +50,25 @@ class U2FKey implements TwoFactorKeyInterface
* @ORM\Column(type="string")
* @var string
**/
protected $keyHandle;
public $keyHandle;
/**
* @ORM\Column(type="string")
* @var string
**/
protected $publicKey;
public $publicKey;
/**
* @ORM\Column(type="text")
* @var string
**/
protected $certificate;
public $certificate;
/**
* @ORM\Column(type="string")
* @var int
**/
protected $counter;
public $counter;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\UserSystem\User", inversedBy="u2fKeys")
@ -150,4 +151,24 @@ class U2FKey implements TwoFactorKeyInterface
{
$this->name = $name;
}
/**
* Gets the user, this U2F key belongs to.
* @return User
*/
public function getUser() : User
{
return $this->user;
}
/**
* Sets the user this U2F key belongs to.
* @param TwoFactorInterface $new_user
* @return $this
*/
public function setUser(TwoFactorInterface $new_user) : self
{
$this->user = $new_user;
return $this;
}
}

View file

@ -0,0 +1,55 @@
<?php
namespace App\EventSubscriber;
use App\Entity\UserSystem\U2FKey;
use Doctrine\ORM\EntityManagerInterface;
use R\U2FTwoFactorBundle\Event\RegisterEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
class U2FRegistrationSubscriber implements EventSubscriberInterface
{
/** @var UrlGeneratorInterface */
private $router;
protected $em;
public function __construct(UrlGeneratorInterface $router, EntityManagerInterface $entityManager)
{
$this->router = $router;
$this->em = $entityManager;
}
// ..
/** @return string[] **/
public static function getSubscribedEvents(): array
{
return array(
'r_u2f_two_factor.register' => 'onRegister',
);
}
public function onRegister(RegisterEvent $event): void
{
$user = $event->getUser();
$registration = $event->getRegistration();
$newKey = new U2FKey();
$newKey->fromRegistrationData($registration);
$newKey->setUser($user);
$newKey->setName($event->getKeyName());
// persist the new key
$this->em->persist($newKey);
$this->em->flush();
// generate new response, here we redirect the user to the fos user
// profile
$response = new RedirectResponse($this->router->generate('user_settings'));
$event->setResponse($response);
}
}

View file

@ -16,6 +16,10 @@
<a class="nav-link" id="backup-tab" data-toggle="tab" href="#tfa-backup" role="tab"
aria-controls="profile" aria-selected="false">{% trans %}tfa.settings.bakup.tab{% endtrans %}</a>
</li>
<li class="nav-item">
<a class="nav-link" id="u2f-tab" data-toggle="tab" href="#tfa-u2f" role="tab"
aria-controls="profile" aria-selected="false">{% trans %}tfa.settings.u2f.tab{% endtrans %}</a>
</li>
</ul>
<div class="tab-content mt-3 mb-3" id="tfa-tabs-content">
<div class="tab-pane fade show active" id="tfa-google" role="tabpanel" aria-labelledby="google-tab">
@ -90,6 +94,37 @@
{% endif %}
</div>
<div class="tab-pane fade" id="tfa-u2f" role="tabpanel" aria-labelledby="u2f-tab">
<p>{% trans %}tfa_u2f.explanation{% endtrans %}</p>
{% if user.u2FKeys is not empty %}
<b>{% trans %}tfa_u2f.table_caption{% endtrans %}:</b>
<table class="table table-striped table-bordered table-sm mt-2">
<thead>
<tr>
<th>#</th>
<th>{% trans %}tfa_u2f.keys.name{% endtrans %}</th>
<th>{% trans %}tfa_u2f.keys.added_date{% endtrans %}</th>
</tr>
</thead>
<tbody>
{% for key in user.u2FKeys %}
<tr>
<td>{{ loop.index }}</td>
<td>{{ key.name }}</td>
<td>{{ key.addedDate | format_datetime }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p><b>{% trans %}tfa_u2f.no_keys_registered{% endtrans %}</b></p>
{% endif %}
<a href="{{ url('club_base_register_u2f') }}" class="btn btn-success"><i class="fas fa-plus-square fa-fw"></i> {% trans %}tfa_u2f.add_new_key{% endtrans %}</a>
</div>
</div>

View file

@ -123,6 +123,10 @@
<script src="{{ preload(asset(js), {as: 'script'}) }}"></script>
{% endfor %}
{% for js in encore_entry_js_files('ru2ftwofactor') %}
<script src="{{ preload(asset(js), {as: 'script'}) }}"></script>
{% endfor %}
{% endblock %}
{% block scripts %}

View file

@ -0,0 +1,58 @@
{% extends "main_card.html.twig" %}
{% block title %}{% trans %}tfa.check.title{% endtrans %}{% endblock %}
{% block card_title %}<i class="fas fa-shield-alt"></i> {% trans %}tfa.check.title{% endtrans %}{% endblock %}
{% block content %}
{% if authenticationError %}
<div class="alert alert-danger" role="alert">
<strong>{{ authenticationError|trans(authenticationErrorData, 'SchebTwoFactorBundle') }}}</strong>
</div>
{% endif %}
{{ parent() }}
{% endblock %}
{% block card_content %}
<ul class="nav nav-pills mb-4">
{% for provider in availableTwoFactorProviders %}
<li class="nav-item">
<a class="nav-link {% if provider == twoFactorProvider %}active{% endif %}"
href="{{ path("2fa_login", {"preferProvider": provider}) }}">{{ 'tfa.provider.'~provider | trans }}</a>
</li>
{% endfor %}
</ul>
{# Display current two-factor provider #}
<form class="form form-horizontal" action="{{ path("2fa_login_check") }}" method="post" {% block form_attributes %}{% endblock %}>
{% block form %}
{% endblock %}
{% if displayTrustedOption %}
<div class="form-group row mt-3">
<div class="offset-3">
<div class="custom-checkbox custom-control ml-2">
<input id="_trusted" class="custom-control-input" type="checkbox" name="{{ trustedParameterName }}" />
<label class="custom-control-label" for="_trusted">{% trans %}tfa.code.trusted_pc{% endtrans %}</label>
</div>
</div>
</div>
{% endif %}
{% if isCsrfProtectionEnabled %}
<input type="hidden" name="{{ csrfParameterName }}" value="{{ csrf_token(csrfTokenId) }}">
{% endif %}
{% block submit_btn %}
<div class="form-group-row">
<div class="offset-3">
<button type="submit" class="btn btn-primary" value="{{ "login"|trans({}, 'SchebTwoFactorBundle') }}">{% trans %}login.btn{% endtrans %}</button>
<a class="ml-2" href="{{ logoutPath }}">{% trans %}user.logout{% endtrans %}</a>
</div>
</div>
{% endblock %}
</form>
{% endblock %}

View file

@ -1,61 +1,14 @@
{% extends "main_card.html.twig" %}
{% extends "security/2fa_base_form.html.twig" %}
{% block title %}{% trans %}tfa.check.title{% endtrans %}{% endblock %}
{% block card_title %}<i class="fas fa-shield-alt"></i> {% trans %}tfa.check.title{% endtrans %}{% endblock %}
{% block content %}
{% if authenticationError %}
<div class="alert alert-danger" role="alert">
<strong>{{ authenticationError|trans(authenticationErrorData, 'SchebTwoFactorBundle') }}}</strong>
{% block form %}
<div class="form-group row">
<label for="_auth_code" class="col-form-label col-3">{% trans %}tfa.check.code.label{% endtrans %}</label>
<div class="col-9">
<input id="_auth_code" class="form-control" type="text" autocomplete="off" name="{{ authCodeParameterName }}" autofocus />
<small id="passwordHelpBlock" class="form-text text-muted">
{% trans %}tfa.check.code.help{% endtrans %}
</small>
</div>
{% endif %}
{{ parent() }}
{% endblock %}
{% block card_content %}
<ul class="nav nav-pills mb-4">
{% for provider in availableTwoFactorProviders %}
<li class="nav-item">
<a class="nav-link {% if provider == twoFactorProvider %}active{% endif %}"
href="{{ path("2fa_login", {"preferProvider": provider}) }}">{{ 'tfa.provider.'~provider | trans }}</a>
</li>
{% endfor %}
</ul>
{# Display current two-factor provider #}
<form class="form form-horizontal" action="{{ path("2fa_login_check") }}" method="post">
<div class="form-group row">
<label for="_auth_code" class="col-form-label col-3">{% trans %}tfa.check.code.label{% endtrans %}</label>
<div class="col-9">
<input id="_auth_code" class="form-control" type="text" autocomplete="off" name="{{ authCodeParameterName }}" autofocus />
<small id="passwordHelpBlock" class="form-text text-muted">
{% trans %}tfa.check.code.help{% endtrans %}
</small>
</div>
</div>
{% if displayTrustedOption %}
<div class="form-group row mt-3">
<div class="offset-3">
<div class="custom-checkbox custom-control ml-2">
<input id="_trusted" class="custom-control-input" type="checkbox" name="{{ trustedParameterName }}" />
<label class="custom-control-label" for="_trusted">{% trans %}tfa.code.trusted_pc{% endtrans %}</label>
</div>
</div>
</div>
{% endif %}
{% if isCsrfProtectionEnabled %}
<input type="hidden" name="{{ csrfParameterName }}" value="{{ csrf_token(csrfTokenId) }}">
{% endif %}
<div class="form-group-row">
<div class="offset-3">
<button type="submit" class="btn btn-primary" value="{{ "login"|trans({}, 'SchebTwoFactorBundle') }}">{% trans %}login.btn{% endtrans %}</button>
<a class="ml-2" href="{{ logoutPath }}">{% trans %}user.logout{% endtrans %}</a>
</div>
</div>
</form>
</div>
{% endblock %}

View file

@ -0,0 +1,16 @@
{% extends "security/2fa_base_form.html.twig" %}
{% block form_attributes %}id="u2fForm" data-action="auth" data-request='{{ authenticationData|raw }}'{% endblock %}
{% block form %}
{% if not app.request.secure %}
<p class="text-warning"><b><i class="fas fa-exclamation-triangle fa-fw"></i> {% trans %}tfa_u2f.http_warning{% endtrans %}</b></p>
{% endif %}
<div id="u2fpressbutton" style="display: none;" class="h4 text-center">{{ 'r_u2f_two_factor.pressbutton'|trans }}</div>
<div id="u2fError"></div>
<p class="widget"><input id="_auth_code" type="hidden" autocomplete="off" name="_auth_code" /></p>
{% endblock %}
{% block submit_btn %}
{% endblock %}

View file

@ -0,0 +1,28 @@
{% extends "main_card.html.twig" %}
{% block card_title %}<i class="fas fa-plus-square fa-fw"></i> {% trans %}tfa_u2f.add_key.title{% endtrans %}{% endblock %}
{% block card_content %}
<p>{% trans %}tfa_u2f.explanation{% endtrans %}</p>
<p><b>{% trans %}tfa_u2f.add_key.backup_hint{% endtrans %}</b></p>
{% if not app.request.secure %}
<p class="text-warning"><b><i class="fas fa-exclamation-triangle fa-fw"></i> {% trans %}tfa_u2f.http_warning{% endtrans %}</b></p>
{% endif %}
<form method="post" class="form" action="{{ path('club_base_register_u2f') }}" id="u2fForm" data-action="reg" data-request='{{ registrationRequest|raw }}'>
<div id="u2fkeyname" class="form-group row">
<div class="col-9">
<input type="text" class="form-control " name="keyName" id="keyName" placeholder="{{ 'r_u2f_two_factor.name'|trans }}"/>
</div>
<div class="col-3">
<button type="button" class="btn btn-success" onclick="u2fauth.register()">{% trans %}tfa_u2f.add_key.add_button{% endtrans %}</button>
</div>
</div>
<div id="u2fpressbutton" style="display: none;" class="text-center h4">{{ 'r_u2f_two_factor.pressbutton'|trans }}</div>
<input type="hidden" name="_auth_code" id="_auth_code" />
<div id="u2fError" style="display: none;" class="text-center h4"></div>
</form>
<a href="{{ url('user_settings') }}">{% trans %}tfa_u2f.add_key.back_to_settings{% endtrans %}</a>
{% endblock %}

View file

@ -40,6 +40,8 @@ Encore
* and one CSS file (e.g. app.css) if you JavaScript imports CSS.
*/
.addEntry('app', './assets/js/app.js')
.addEntry('ru2ftwofactor', './assets/js/u2f_auth.js')
//.addEntry('ajaxUI', './assets/ts_src/ajax_ui.ts')
//.addEntry('page1', './assets/js/page1.js')
//.addEntry('page2', './assets/js/page2.js')

View file

@ -6702,6 +6702,11 @@ typescript@^3.3.4000:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.3.tgz#b36840668a16458a7025b9eabfad11b66ab85c69"
integrity sha512-Mcr/Qk7hXqFBXMN7p7Lusj1ktCBydylfQM/FZCk5glCNQJrCUKPkMHdo9R0MTFWsC/4kPFvDS0fDPvukfCkFsw==
u2f-api@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/u2f-api/-/u2f-api-1.1.1.tgz#594f600ff7bb49e8bb03e9f533e1f7bfc95f2e0f"
integrity sha512-tbmMBmg9eaFv+cdcBwja/hbpdXwcvBV0YSQh674nmP3HI0hNWcNAp9LRJ0jl9HGWLF7gF/3UKHzIxlXCW8j7kw==
unicode-canonical-property-names-ecmascript@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818"