Comments (16)
The main use of the user handle is to identify the user account in such authentication ceremonies, but the credential ID could be used instead. The main differences are that the credential ID is chosen by the authenticator and is unique for each credential, while the user handle is chosen by the Relying Party and ought to be the same for all credentials registered to the same user account.
There's also a subtle nuance in here that credential IDs are chosen by the authenticator, but user IDs are chosen by the RP. That makes user IDs "better" from the RP's perspective because it's a value that the RP controls and can predict the shape of vs the variability observable in credential ID lengths.
For the secondary use case, it reads like the spec suggests to use the same user id for all authenticators that belong to the same user. This would imply that using random user ids is not ideal?
One of the main goals of using random identifiers for user.id
is to preserve user privacy by not using email addresses or usernames that could be used to link a user across multiple sites, even sites that don't use passkeys at all! I missed that the spec says "ought to be the same for all credentials registered to the same user account", but honestly if you're going this route (vs specifying user IDs yourself) you could just as easily use a lookup table to handle random user IDs for every credential that all link back internally to a single DB user ID 🤔
from simplewebauthn.
(That said I still personally think it's okay to simply look up users by credential ID; I haven't heard of anyone running into issues with this approach. It's stuff like this that makes it hard to suggest the "best" way to do anything with SimpleWebAuthn: RP use cases and risk models are so varied I try to find a middle ground that'll not make a ton of work for any one type of RP...)
from simplewebauthn.
Thank you for the detailed answer and your continued work on the SimpleWebAuthn library!
I decided to continue using the user ids from database (incremental primary keys) as the user handle. They are not random and allow to identify a user within my application (at least find the associated username), but I do not consider this to be a privacy issue for my application. There is no email or real names revealed.
For authentication I continue looking up passkeys in my database based on the base64 credential id, instead of the user handle. This seems to be fine by the spec and is working fine. The user handle is not guaranteed to exist or exists in a different format for authenticators, that have been generated using previous registration options. The credential id is always there.
from simplewebauthn.
The user handle is not guaranteed to exist or exists in a different format for authenticators, that have been generated using previous registration options. The credential id is always there.
This is another reason I started looking up users by credential ID and never really looked back. Nowadays userHandle
is almost always being returned, but not even L3 is going to make userHandle
required so as of 2024 RPs will still have to prepare to handle its absence. And when userHandle
is unavailable, how else can you look up the user? By credential ID...such is the way this stuff goes 😂
from simplewebauthn.
I am using them now for the userID. I suspect that they would fit within your proposed 64 byte Uint8Array type, but have not tested to determine if this would break my code.
Please consider using ULIDs for your userID generation given their benefits in data storage: more info
from simplewebauthn.
I still think by default it's a good idea for me to generate completely random values when userID
is omitted. However in your case there's nothing about what I'm proposing that would prevent you from using ULIDs for userID
, so long as you figured out a way to convert them to a Uint8Array
that you're comfortable with.
from simplewebauthn.
so long as you figured out a way to convert them to a Uint8Array that you're comfortable with.
I have not...
I'm having some trouble with this - I let py_webauthn generate a user id from the backend (it gets encoded to "OPclcKTH6cjyjoRncpBrvKaepPz4eagbzFtOVnYCmANpUx0Vntm1lzlabOri5BF97CLNfTL440SIhbqwd459eQ") and then I'm converting that to a Uint8Array:
// Convert base64url encoded bytes into a Uint8Array. Used by passkeys.
const decodeBase64 = (encoded) => {
return new Uint8Array(atob(encoded)
.split('')
.map((c) => c.charCodeAt(0)));
};
const decodeBase64Url = (input) => {
try {
return decodeBase64(input.replace(/-/g, '+').replace(/_/g, '/').replace(/\s/g, ''));
}
catch (_a) {
throw new TypeError('The input to be decoded is not correctly encoded.');
}
};
I get 64 elements:
But then startRegistration() has an exception "TypeError: User ID was not between 1 and 64 characters".
Any tips?
from simplewebauthn.
I'm having some trouble with this - I let py_webauthn generate a user id from the backend (it gets encoded to "OPclcKTH6cjyjoRncpBrvKaepPz4eagbzFtOVnYCmANpUx0Vntm1lzlabOri5BF97CLNfTL440SIhbqwd459eQ") and then I'm converting that to a Uint8Array
@rimu Are you feeding output from generate_registration_options()
in py_webauthn to startRegistration()
in @simplewebauthn/browser? If so you're intended to pass in the JSON options as-is and let startRegistration()
take care of converting a value like OPclcKTH6cjyjoRncpBrvKaepPz4eagbzFtOVnYCmANpUx0Vntm1lzlabOri5BF97CLNfTL440SIhbqwd459eQ
into the bytes that navigator.credentials.create()
expects.
from simplewebauthn.
Ah, yes, now I can see where startRegistration does the conversion. I still get the same error, though.
I'm using 9.0.1 which is a bit old. That is the version that <script src="https://unpkg.com/@simplewebauthn/browser/dist/bundle/index.umd.min.js"></script>
provides. Would it help if I was using 9.0.3?
from simplewebauthn.
Solved by using a shorter user_id: duo-labs/py_webauthn#199 (comment)
from simplewebauthn.
These changes are now available in the recently-published @simplewebauthn/[email protected], @simplewebauthn/[email protected], and @simplewebauthn/[email protected] ✌️
from simplewebauthn.
@MasterKale Is it required to save the random generated user ID value in the database? From my understanding, it is randomly generated in generateRegistrationOptions
but then it is never validated in the future. Do I miss something?
I previously used internal user ids (ascending integer ids) for the userId field, but I think I can just ignore the field entirely with v10? What is the value even for? Does it need to be known in the backend?
from simplewebauthn.
@MasterKale Is it required to save the random generated user ID value in the database? From my understanding, it is randomly generated in
generateRegistrationOptions
but then it is never validated in the future. Do I miss something? I previously used internal user ids (ascending integer ids) for the userId field, but I think I can just ignore the field entirely with v10? What is the value even for? Does it need to be known in the backend?
@P4sca1, The userId is the userId, if you aren't using the userId how are you identifying who the user is?
from simplewebauthn.
I create a HTTP session when the authentication / registration process starts which contains the user id and WebAuthn challenge.
When verifying passkey authentication request, I select the Passkey with the given base64CredentialId. The resulting Passkey object contains the userId.
from simplewebauthn.
Okay, I found it in the spec (emphasis mine):
A user handle is an identifier for a user account, specified by the Relying Party as user.id during registration. Discoverable credentials store this identifier and MUST return it as response.userHandle in authentication ceremonies started with an empty allowCredentials argument.
The main use of the user handle is to identify the user account in such authentication ceremonies, but the credential ID could be used instead. The main differences are that the credential ID is chosen by the authenticator and is unique for each credential, while the user handle is chosen by the Relying Party and ought to be the same for all credentials registered to the same user account.
Authenticators map pairs of RP ID and user handle to public key credential sources. As a consequence, an authenticator will store at most one discoverable credential per user handle per Relying Party. Therefore a secondary use of the user handle is to allow authenticators to know when to replace an existing discoverable credential with a new one during the registration ceremony.
https://www.w3.org/TR/webauthn-3/#user-handle
For the secondary use case, it reads like the spec suggests to use the same user id for all authenticators that belong to the same user. This would imply that using random user ids is not ideal?
from simplewebauthn.
From https://www.w3.org/TR/webauthn-3/#sctn-user-handle-privacy:
It is RECOMMENDED to let the user handle be 64 random bytes, and store this value in the user account.
from simplewebauthn.
Related Issues (20)
- ignore `node:crypto` import with webpack in `@simplewebauthn/server` HOT 8
- Update minimum Node support to v20+ HOT 3
- library reports lack of support to webauthn in mobile chrome but webauthn seems to work if using reference site HOT 8
- Using Android devices without non-Google Password managers fail to authenticate HOT 11
- Change signature of `decodeClientDataJSON` to accept `Base64URLString` as paramater HOT 2
- Improve ergonomics of `excludeCredentials` and `allowCredentials` HOT 3
- Update `getWebCrypto()` for Node 20 HOT 2
- cross-platform authenticator usage on android 14 HOT 1
- Default `userDisplayName` to empty string when not specified HOT 1
- Handle undefined `PublicKeyCredential` in `browserSupportsWebAuthnAutofill` helper HOT 1
- Discrepancy between example code and docs HOT 4
- get public key as string
- Make rpID required when generating authentication options HOT 1
- Clean up some docstrings in @simplewebauthn/server
- Rename `AuthenticatorDevice` type and usage
- Parsing the attestation data explicitly HOT 3
- Changed userHandle behavior v10 breaks definitely with existing registrations HOT 2
- RequireUserVerification Is Not Documented HOT 2
- Investigate impact of Deno moving its std library to JSR
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from simplewebauthn.