mirror of
https://github.com/gravitational/teleport.git
synced 2026-09-24 16:17:11 +08:00
Update WebAuthn and U2F dependencies (#16572)
Update `duo-labs/webauthn` up to `20220122034320`, which is the latest version we can get without dipping into dependency hell (`etcd` and `opentelemetry` woes ensue after [2365c59d9f][1]). `tstranex` could be dropped for a while now (we moved on to WebAuthn-like interfaces for mocks). `cfssl` was only imported due to what I assume was an IDE mishap. I've elected to keep `fxamacker/cbor`, instead of trying to move to [webauthncbor][2]. fxamacker is solid, past v0, seems more appropriate for client-side libs and still backs webauthncbor. There are no updates for `flynn/hid` and `flynn/u2f`. Release notes for fxamacker/cbor: https://github.com/fxamacker/cbor/releases/tag/v2.4.0. [1]: https://github.com/duo-labs/webauthn/commit/2365c59d9fb666cb5f5b6bbae86910632198a8d4 [2]: https://pkg.go.dev/github.com/duo-labs/webauthn@v0.0.0-20220815211337-00c9fb5711f5/protocol/webauthncbor * Drop tstranex/u2f dependency * Drop direct dependency to cloudflare/cfssl * Update fxamacker/cbor/v2 to v2.4.0 * Update duo-labs/webauthn to 2022-01-22 * Fix: Make sure all credentials are set in the user * Simplify: Drop now unnecessary AuthenticationSelection copy
This commit is contained in:
@@ -27,18 +27,17 @@ require (
|
||||
github.com/aws/aws-sdk-go-v2/service/ec2 v1.16.0
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.10.0
|
||||
github.com/beevik/etree v1.1.0
|
||||
github.com/cloudflare/cfssl v0.0.0-20190726000631-633726f6bcb7
|
||||
github.com/coreos/go-oidc v2.1.0+incompatible
|
||||
github.com/coreos/go-semver v0.3.0
|
||||
github.com/creack/pty v1.1.18
|
||||
github.com/denisenkom/go-mssqldb v0.11.0
|
||||
github.com/duo-labs/webauthn v0.0.0-20210727191636-9f1b88ef44cc
|
||||
github.com/duo-labs/webauthn v0.0.0-20220122034320-81aea484c951
|
||||
github.com/dustin/go-humanize v1.0.0
|
||||
github.com/elastic/go-elasticsearch/v8 v8.4.0
|
||||
github.com/flynn/hid v0.0.0-20190502022136-f1b9b6cc019a
|
||||
github.com/flynn/u2f v0.0.0-20180613185708-15554eb68e5d
|
||||
github.com/fsouza/fake-gcs-server v1.19.5
|
||||
github.com/fxamacker/cbor/v2 v2.3.0
|
||||
github.com/fxamacker/cbor/v2 v2.4.0
|
||||
github.com/ghodss/yaml v1.0.0
|
||||
github.com/gizak/termui/v3 v3.1.0
|
||||
github.com/go-ldap/ldap/v3 v3.4.1
|
||||
@@ -96,7 +95,6 @@ require (
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/snowflakedb/gosnowflake v1.6.9
|
||||
github.com/stretchr/testify v1.8.0
|
||||
github.com/tstranex/u2f v0.0.0-20160508205855-eb799ce68da4
|
||||
github.com/ucarion/urlpath v0.0.0-20200424170820-7ccc79b76bbb
|
||||
github.com/vulcand/predicate v1.2.0
|
||||
go.etcd.io/etcd/api/v3 v3.5.1
|
||||
@@ -185,10 +183,10 @@ require (
|
||||
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 // indirect
|
||||
github.com/cloudflare/cfssl v0.0.0-20190726000631-633726f6bcb7 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/elastic/elastic-transport-go/v8 v8.1.0 // indirect
|
||||
github.com/emicklei/go-restful v2.9.5+incompatible // indirect
|
||||
@@ -207,6 +205,7 @@ require (
|
||||
github.com/go-openapi/jsonreference v0.19.5 // indirect
|
||||
github.com/go-openapi/swag v0.19.14 // indirect
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.2.0 // indirect
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect
|
||||
github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
@@ -274,7 +273,6 @@ require (
|
||||
github.com/rogpeppe/go-internal v1.8.0 // indirect
|
||||
github.com/russross/blackfriday v1.5.2 // indirect
|
||||
github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 // indirect
|
||||
github.com/satori/go.uuid v1.2.0 // indirect
|
||||
github.com/shabbyrobe/gocovmerge v0.0.0-20190829150210-3e036491d500 // indirect
|
||||
github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726 // indirect
|
||||
github.com/siddontang/go-log v0.0.0-20180807004314-8d05993dda07 // indirect
|
||||
|
||||
@@ -300,8 +300,8 @@ github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
|
||||
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
|
||||
github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||
github.com/duo-labs/webauthn v0.0.0-20210727191636-9f1b88ef44cc h1:mLNknBMRNrYNf16wFFUyhSAe1tISZN7oAfal4CZ2OxY=
|
||||
github.com/duo-labs/webauthn v0.0.0-20210727191636-9f1b88ef44cc/go.mod h1:/X2OJiJxjQ7alqWZqX9EtBTmZc+4qQ0LvZ1k5wP67RM=
|
||||
github.com/duo-labs/webauthn v0.0.0-20220122034320-81aea484c951 h1:17esZ09oW+29rklBtCVphIguql2u3NxYH2OasFPPZoo=
|
||||
github.com/duo-labs/webauthn v0.0.0-20220122034320-81aea484c951/go.mod h1:nHy3JdztZWcsjenDeBuE8gn171OAwg12LBN027UP5AE=
|
||||
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
@@ -361,8 +361,8 @@ github.com/fsouza/fake-gcs-server v1.19.5 h1:YTBDIWtCSF6T9x5Hm0lw0clIF7/XTZIQ58g
|
||||
github.com/fsouza/fake-gcs-server v1.19.5/go.mod h1:Jd6DuVGNvNXC+pEoAbRDihNaEIjIW9UuRVPEvBhHJek=
|
||||
github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
|
||||
github.com/fxamacker/cbor/v2 v2.2.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
|
||||
github.com/fxamacker/cbor/v2 v2.3.0 h1:aM45YGMctNakddNNAezPxDUpv38j44Abh+hifNuqXik=
|
||||
github.com/fxamacker/cbor/v2 v2.3.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
|
||||
github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88=
|
||||
github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
|
||||
github.com/gabriel-vasile/mimetype v1.4.0 h1:Cn9dkdYsMIu56tGho+fqzh7XmvY2YyGU0FnbhiOsEro=
|
||||
github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8=
|
||||
github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg=
|
||||
@@ -426,11 +426,12 @@ github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14j
|
||||
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
|
||||
github.com/golang-jwt/jwt v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfEP4Q1lOd9Z/+c=
|
||||
github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
github.com/golang-jwt/jwt/v4 v4.1.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
||||
github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU=
|
||||
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188 h1:+eHOFJl1BaXrQxKX+T06f78590z4qA2ZzBTqahsKSE4=
|
||||
@@ -1022,7 +1023,6 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb
|
||||
github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 h1:GHRpF1pTW19a8tTFrMLUcfWwyC0pnifVo2ClaLq+hP8=
|
||||
github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8=
|
||||
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
|
||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
@@ -1097,8 +1097,6 @@ github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhV
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tstranex/u2f v0.0.0-20160508205855-eb799ce68da4 h1:aR+lGR8m0zBjvDlHkHOCmdsk79ipIPeiP75GqUlywKM=
|
||||
github.com/tstranex/u2f v0.0.0-20160508205855-eb799ce68da4/go.mod h1:eahSLaqAS0zsIEv80+vXT7WanXs7MQQDg3j3wGBSayo=
|
||||
github.com/ucarion/urlpath v0.0.0-20200424170820-7ccc79b76bbb h1:Ywfo8sUltxogBpFuMOFRrrSifO788kAFxmvVw31PtQQ=
|
||||
github.com/ucarion/urlpath v0.0.0-20200424170820-7ccc79b76bbb/go.mod h1:ikPs9bRWicNw3S7XpJ8sK/smGwU9WcSVU3dy9qahYBM=
|
||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// go:build linux
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
@@ -29,7 +28,6 @@ import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/cloudflare/cfssl/log"
|
||||
"github.com/gravitational/teleport/api/types"
|
||||
"github.com/gravitational/teleport/lib/backend"
|
||||
"github.com/gravitational/teleport/lib/backend/lite"
|
||||
@@ -39,6 +37,8 @@ import (
|
||||
"github.com/gravitational/teleport/lib/utils/host"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const testuser = "teleport-testuser"
|
||||
|
||||
@@ -22,7 +22,6 @@ package mocku2f
|
||||
*/
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
@@ -30,16 +29,12 @@ import (
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/duo-labs/webauthn/protocol"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/tstranex/u2f"
|
||||
)
|
||||
|
||||
// u2fRegistrationFlags is fixed by the U2F standard.
|
||||
@@ -74,19 +69,6 @@ type Key struct {
|
||||
counter uint32
|
||||
}
|
||||
|
||||
// The "websafe-base64 encoding" in the U2F specifications removes the padding
|
||||
func decodeBase64(s string) ([]byte, error) {
|
||||
for i := 0; i < len(s)%4; i++ {
|
||||
s += "="
|
||||
}
|
||||
return base64.URLEncoding.DecodeString(s)
|
||||
}
|
||||
|
||||
func encodeBase64(buf []byte) string {
|
||||
s := base64.URLEncoding.EncodeToString(buf)
|
||||
return strings.TrimRight(s, "=")
|
||||
}
|
||||
|
||||
func selfSignPublicKey(keyToSign *ecdsa.PublicKey) (cert []byte, err error) {
|
||||
caPrivateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
@@ -151,31 +133,6 @@ func (muk *Key) SetPasswordless() {
|
||||
muk.SetUV = true // UV required for passwordless.
|
||||
}
|
||||
|
||||
func (muk *Key) RegisterResponse(req *u2f.RegisterRequest) (*u2f.RegisterResponse, error) {
|
||||
appIDHash := sha256.Sum256([]byte(req.AppID))
|
||||
|
||||
clientData := u2f.ClientData{
|
||||
Typ: "navigator.id.finishEnrollment",
|
||||
Challenge: req.Challenge,
|
||||
Origin: req.AppID,
|
||||
}
|
||||
clientDataJSON, err := json.Marshal(clientData)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
clientDataHash := sha256.Sum256(clientDataJSON)
|
||||
|
||||
res, err := muk.signRegister(appIDHash[:], clientDataHash[:])
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
return &u2f.RegisterResponse{
|
||||
RegistrationData: encodeBase64(res.RawResp),
|
||||
ClientData: encodeBase64(clientDataJSON),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// RegisterRaw signs low-level U2F registration data.
|
||||
// Most callers should use either RegisterResponse or SignCredentialCreation.
|
||||
func (muk *Key) RegisterRaw(appHash, challengeHash []byte) ([]byte, error) {
|
||||
@@ -229,39 +186,6 @@ func (muk *Key) signRegister(appIDHash, clientDataHash []byte) (*signRegisterRes
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (muk *Key) SignResponse(req *u2f.SignRequest) (*u2f.SignResponse, error) {
|
||||
rawKeyHandle, err := decodeBase64(req.KeyHandle)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
if !bytes.Equal(rawKeyHandle, muk.KeyHandle) {
|
||||
return nil, trace.CompareFailed("wrong keyHandle")
|
||||
}
|
||||
appIDHash := sha256.Sum256([]byte(req.AppID))
|
||||
|
||||
clientData := u2f.ClientData{
|
||||
Typ: "navigator.id.getAssertion",
|
||||
Challenge: req.Challenge,
|
||||
Origin: req.AppID,
|
||||
}
|
||||
clientDataJSON, err := json.Marshal(clientData)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
clientDataHash := sha256.Sum256(clientDataJSON)
|
||||
|
||||
res, err := muk.signAuthn(appIDHash[:], clientDataHash[:])
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
return &u2f.SignResponse{
|
||||
KeyHandle: req.KeyHandle,
|
||||
SignatureData: encodeBase64(res.SignData),
|
||||
ClientData: encodeBase64(clientDataJSON),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// AuthenticateRaw signs low-level U2F authentication data.
|
||||
// Most callers should use either SignResponse or SignAssertion.
|
||||
func (muk *Key) AuthenticateRaw(appHash, challengeHash []byte) ([]byte, error) {
|
||||
|
||||
@@ -256,6 +256,17 @@ func (f *loginFlow) finish(ctx context.Context, user string, resp *CredentialAss
|
||||
}
|
||||
sessionData := sessionFromPB(sessionDataPB)
|
||||
|
||||
// Make sure _all_ credentials in the session are accounted for by the user.
|
||||
// webauthn.ValidateLogin requires it.
|
||||
for _, allowedCred := range sessionData.AllowedCredentialIDs {
|
||||
if bytes.Equal(parsedResp.RawID, allowedCred) {
|
||||
continue
|
||||
}
|
||||
u.credentials = append(u.credentials, wan.Credential{
|
||||
ID: allowedCred,
|
||||
})
|
||||
}
|
||||
|
||||
// Create a WebAuthn matching the expected RPID and Origin, then verify the
|
||||
// signed challenge.
|
||||
web, err := newWebAuthn(webAuthnParams{
|
||||
|
||||
@@ -175,15 +175,11 @@ func (f *RegistrationFlow) Begin(ctx context.Context, user string, passwordless
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
credentialCreation, sessionData, err := web.BeginRegistration(u, wan.WithExclusions(exclusions))
|
||||
cc, sessionData, err := web.BeginRegistration(u, wan.WithExclusions(exclusions))
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
// Copy settings manually, the framework doesn't do it.
|
||||
credentialCreation.Response.AuthenticatorSelection = web.Config.AuthenticatorSelection
|
||||
sessionData.UserVerification = web.Config.AuthenticatorSelection.UserVerification
|
||||
|
||||
// TODO(codingllama): Send U2F App ID back in creation requests too. Useful to
|
||||
// detect duplicate devices.
|
||||
|
||||
@@ -195,7 +191,7 @@ func (f *RegistrationFlow) Begin(ctx context.Context, user string, passwordless
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
return (*CredentialCreation)(credentialCreation), nil
|
||||
return (*CredentialCreation)(cc), nil
|
||||
}
|
||||
|
||||
func upsertOrGetWebID(ctx context.Context, user string, identity RegistrationIdentity) ([]byte, error) {
|
||||
|
||||
Reference in New Issue
Block a user