use different webauthn for the browser
This commit is contained in:
parent
9caab0ac94
commit
c6af2c8a98
@ -234,7 +234,7 @@
|
|||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@teamhanko/hanko-webauthn@latest/dist/browser-global/hanko-webauthn.browser-global.js"></script>
|
<script type="module" src="https://unpkg.com/@github/webauthn-json@2.0.2/dist/esm/webauthn-json.browser-ponyfill.js"></script>
|
||||||
|
|
||||||
<script type="module" src="/static/admin.js"></script>
|
<script type="module" src="/static/admin.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
<button id="rex">Abrir</button>
|
<button id="rex">Abrir</button>
|
||||||
</form>
|
</form>
|
||||||
</main>
|
</main>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@teamhanko/hanko-webauthn@latest/dist/browser-global/hanko-webauthn.browser-global.js"></script>
|
<script type="module" src="https://unpkg.com/@github/webauthn-json@2.0.2/dist/esm/webauthn-json.browser-ponyfill.js"></script>
|
||||||
<script type="module" src="/static/index.js" async="async"></script>
|
<script type="module" src="/static/index.js" async="async"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -38,6 +38,8 @@ type HTTPConfig struct {
|
|||||||
Listen string `yaml:"listen"`
|
Listen string `yaml:"listen"`
|
||||||
// Origin describes the http origins to allow
|
// Origin describes the http origins to allow
|
||||||
Origin string `yaml:"origin"`
|
Origin string `yaml:"origin"`
|
||||||
|
// Protocol specifies the protocol for the webauthn origin
|
||||||
|
Protocol string `yaml:"protocol"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
@ -54,6 +56,7 @@ func ConfigDefaults(dbPath string) *Config {
|
|||||||
HTTP: &HTTPConfig{
|
HTTP: &HTTPConfig{
|
||||||
Listen: "localhost:8000",
|
Listen: "localhost:8000",
|
||||||
Origin: "localhost",
|
Origin: "localhost",
|
||||||
|
Protocol: "http",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -178,8 +181,9 @@ func Initialize(config *Config) (http.Handler, error) {
|
|||||||
wan, err := webauthn.New(&webauthn.Config{
|
wan, err := webauthn.New(&webauthn.Config{
|
||||||
RPDisplayName: config.Name,
|
RPDisplayName: config.Name,
|
||||||
RPID: config.HTTP.Origin,
|
RPID: config.HTTP.Origin,
|
||||||
RPOrigins: []string{config.HTTP.Origin},
|
RPOrigins: []string{config.HTTP.Protocol + "://" + config.HTTP.Origin},
|
||||||
// RPIcon: "https://go-webauthn.local/logo.png",
|
// For dev:
|
||||||
|
// RPOrigins: []string{config.HTTP.Protocol + "://" + config.HTTP.Listen},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// Copyright © 2022 Roberto Hidalgo <nidito@un.rob.mx>
|
// Copyright © 2022 Roberto Hidalgo <nidito@un.rob.mx>
|
||||||
const { create: createCredentials, get: getCredentials } = hankoWebAuthn;
|
import * as webauthnJSON from 'https://unpkg.com/@github/webauthn-json@2.0.2/dist/esm/webauthn-json.browser-ponyfill.js'
|
||||||
|
|
||||||
const charsToEncode = /[\u007f-\uffff]/g;
|
const charsToEncode = /[\u007f-\uffff]/g;
|
||||||
function JSONtob64(data) {
|
function JSONtob64(data) {
|
||||||
return btoa(JSON.stringify(data).replace(charsToEncode, (c) => '\\u'+('000'+c.charCodeAt(0).toString(16)).slice(-4)))
|
return btoa(JSON.stringify(data).replace(charsToEncode, (c) => '\\u'+('000'+c.charCodeAt(0).toString(16)).slice(-4)))
|
||||||
@ -21,7 +20,6 @@ export async function withAuth(target, config) {
|
|||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(Object.fromEntries(response.headers))
|
|
||||||
const challengeHeader = response.headers.get("webauthn")
|
const challengeHeader = response.headers.get("webauthn")
|
||||||
if (!challengeHeader || challengeHeader == "") {
|
if (!challengeHeader || challengeHeader == "") {
|
||||||
console.debug(`webauthn: success without auth`)
|
console.debug(`webauthn: success without auth`)
|
||||||
@ -42,20 +40,41 @@ export async function withAuth(target, config) {
|
|||||||
// we try to do that
|
// we try to do that
|
||||||
await register(challenge, target)
|
await register(challenge, target)
|
||||||
// and retry the original request if successful
|
// and retry the original request if successful
|
||||||
return await withAuth(target, config)
|
return await new Promise((res, rej) => {
|
||||||
|
setTimeout(async () => {
|
||||||
|
try {
|
||||||
|
res(await withAuth(target, config))
|
||||||
|
} catch(err) {
|
||||||
|
rej(err)
|
||||||
|
}
|
||||||
|
}, 1000)
|
||||||
|
})
|
||||||
} else if (step == "login") {
|
} else if (step == "login") {
|
||||||
// server told us to use existing credential for request
|
// server told us to use existing credential for request
|
||||||
return await login(challenge, target, config)
|
return await login(challenge, target, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
throw `Unknown webauthn step: <${kind}>`
|
throw `Unknown webauthn step: <${step}>`
|
||||||
}
|
}
|
||||||
|
|
||||||
async function register(challenge) {
|
async function register(challenge) {
|
||||||
console.info("webauthn: creating credentials")
|
console.info("webauthn: initializing registration from challenge")
|
||||||
const credential = await createCredentials(challenge);
|
console.dir(challenge)
|
||||||
|
|
||||||
|
const parsed = webauthnJSON.parseCreationOptionsFromJSON(challenge)
|
||||||
|
console.debug("webauthn: parsed challenge")
|
||||||
|
console.dir(parsed)
|
||||||
|
|
||||||
|
console.info("webauthn: issuing credential creation request to browser")
|
||||||
|
let credential
|
||||||
|
try {
|
||||||
|
credential = await webauthnJSON.create(parsed);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("sigh", err)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
console.debug(`webauthn: registering credentials with server: ${JSON.stringify(credential)}`)
|
console.debug(`webauthn: registering credentials with server: ${JSON.stringify(credential)}`)
|
||||||
|
|
||||||
let response = await window.fetch("/api/webauthn/register", {
|
let response = await window.fetch("/api/webauthn/register", {
|
||||||
credentials: "include",
|
credentials: "include",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@ -81,8 +100,14 @@ async function register(challenge) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function login(challenge, target, config) {
|
async function login(challenge, target, config) {
|
||||||
console.info("webauthn: fetching stored client credentials")
|
console.info("webauthn: initializing login from challenge")
|
||||||
const credential = await getCredentials(challenge);
|
console.dir(challenge)
|
||||||
|
const parsed = webauthnJSON.parseRequestOptionsFromJSON(challenge)
|
||||||
|
console.debug("webauthn: parsed challenge")
|
||||||
|
console.dir(parsed)
|
||||||
|
|
||||||
|
console.debug("webauthn: fetching stored client credentials")
|
||||||
|
let credential = await webauthnJSON.get(parsed);
|
||||||
|
|
||||||
config.credentials = "include"
|
config.credentials = "include"
|
||||||
config.headers = config.headers || {}
|
config.headers = config.headers || {}
|
||||||
|
@ -40,7 +40,7 @@ type User struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *User) WebAuthnID() []byte {
|
func (u *User) WebAuthnID() []byte {
|
||||||
return []byte(fmt.Sprintf("%d", u.ID))
|
return []byte(fmt.Sprintf("%d-%s", u.ID, u.Handle))
|
||||||
}
|
}
|
||||||
|
|
||||||
// User Name according to the Relying Party
|
// User Name according to the Relying Party
|
||||||
|
Loading…
Reference in New Issue
Block a user