Comments (8)
This sounds like a legitimate use case for specifying your own challenge argument when calling generateRegistrationOptions(). But is there something preventing you from persisting options.challenge afterwards to use later in verifyRegistrationResponse()?
The lift is pretty low to do this! I think the larger callout here is that two separate parties ended up coming to the same workaround, which felt like the most "natural" fix for us.
My general thought process for challenge-based auth is:
- Generate and store a reference to the challenge
- Build the message with generated challenge (
generateRegistrationOptions
) - Return the message for the client to sign
My choice to persist my own challenge immediately after generation felt more natural, and I view generateRegistrationOptions
as more of a challenge formatter than something that will transform the input challenge.
This isn't something that I was planning on calling out, but I noticed the issue already existed, so I figured it may be helpful to add my own 2c here.
For the majority of users, if you use SimpleWebAuthn as documented then you won't have to know to base64url-encode your challenge value when you pass it in as expectedChallenge.
Agreed!
from simplewebauthn.
Thank you for the feedback @payton and @damianobarbati, honestly. It's good for me to review older code like this from time to time and practical problems like the one that you both independently came upon are perfect for helping me frame potential improvements.
My general thought process for challenge-based auth is:
- Generate and store a reference to the challenge
- Build the message with generated challenge (
generateRegistrationOptions
)- Return the message for the client to sign
This totally makes sense to me. I think I can do better for more advanced use cases like yours' without sacrificing the ease-of-use for the majority of users who don't want/need to otherwise bother with challenge generation.
I'll noodle on this over the holidays, see what comes to mind 🦃 🎄
from simplewebauthn.
Are you using something other than @simplewebauthn/browser on the front end to process and pass the options to navigator.credentials.create()
? Because I did the following and it worked just fine:
// Options
const options = await generateRegistrationOptions({
// ...
challenge: 'hello',
});
req.session.currentChallenge = options.challenge;
// Browser
// Feed `options` into @simplewebauthn/browser's `startRegistration()`
// Server
const verification = await verifyRegistrationResponse({
// ...
expectedChallenge: isoBase64URL.fromString('hello'),
});
from simplewebauthn.
Though honestly I hardly ever recommend specifying your own value for challenge
. It's supposed to be unique on every registration and authentication, and never reused, so I advise letting my library handle it by omitting a value for challenge
when calling generateRegistrationOptions()
.
from simplewebauthn.
Hey @MasterKale - First of all, thank you for building this out! It's such a strong tool that abstracts webauthn beautifully.
On the topic of this issue, I had similar initial thoughts to @damianobarbati. I was mostly surprised by the interface requiring me to base64 encode the challenge when it was not base64 encoded on calling generateRegistrationOptions()
.
Though honestly I hardly ever recommend specifying your own value for challenge.
In my case, I have an existing challenge flow, so it's more natural to bring my own challenge (that's not to say it couldn't be refactored, but not ideal).
from simplewebauthn.
2023-Me is looking at the challenge
argument in generateRegistrationOptions()
and wondering why it was only ever a string
until recently, as it leads to a weird use case like what you're describing here.
What is the intended way to verify the challenge defined plain in
generateRegistrationOptions
?
Shouldn'tverifyRegistrationResponse
decode properly the challenge returned back?
However, after reviewing my own documentation...
- https://simplewebauthn.dev/docs/packages/server#1-generate-registration-options
- https://simplewebauthn.dev/docs/packages/server#2-verify-registration-response
- https://github.com/MasterKale/SimpleWebAuthn/blob/master/example/index.ts#L155-L159
...I've always advised SimpleWebAuthn users to persist options.challenge
out of generateRegistrationOptions()
to then feed back into verifyRegistrationResponse()
as expectedChallenge
.
In my case, I have an existing challenge flow, so it's more natural to bring my own challenge (that's not to say it couldn't be refactored, but not ideal).
This sounds like a legitimate use case for specifying your own challenge
argument when calling generateRegistrationOptions()
. But is there something preventing you from persisting options.challenge
afterwards to use later in verifyRegistrationResponse()
?
For the majority of users, if you use SimpleWebAuthn as documented then you won't have to know to base64url-encode your challenge
value when you pass it in as expectedChallenge
. I can think about ways to make it easier to reuse a value between challenge
and expectedChallenge
, but I still think it's simpler for my library users to disregard the value of options.challenge
and simply dump it back into verifyRegistrationResponse()
as I document.
from simplewebauthn.
I did some brainstorming today and I can't yet see a win-win scenario.
First I considered stopping base64url-encoding challenge
values in generate...Options()
methods when they're strings. That would enable RP's, like you, to specify the same value (e.g. "hello"
) for challenge
and expectedChallenge
when calling @simplewebauthn/server methods.
However, authenticators always return the challenge
bytes as base64url-encoded in clientDataJSON
:
Thinking ahead a little bit, I could see this being a potential trade-off during debugging. It's easy to take a WebAuthn response, paste it into https://debugger.simplewebauthn.dev, and copy-paste the value needed for the expectedChallenge
argument. If expectedChallenge
was modified to be the base64url-decoded value instead, though, then that would require RP devs to know to decode clientDataJSON.challenge
to confirm the challenge they passed in as challenge
when generating options. I guess I could update the debugger to do that work for me (...and maybe this particular issue I'm foreseeing is a "me" problem that'll make it less straightforward for me to confirm issues that people open here 🤔)
Looking at the history of this project, the expectedChallenge
arguments in both verification methods have been documented in the code with this description since July 2020 (way back in #42):
The base64url-encoded
options.challenge
returned bygenerateRegistrationOptions()
(emphasis mine)
Making changes to accommodate your specific use case isn't a clear winner in my book, so at this moment in time I'm not inclined to make any functional changes to support it. As
I'm open to suggestions for how I might improve documentation to communicate the need to base64url-encode custom challenges, though. Perhaps something under Advanced Guides in the docs? It could more explicitly document what we talked about in here, about needing to use isoBase64URL.fromString()
when feeding your custom string value for challenge
in as expectedChallenge
later 🤔
from simplewebauthn.
I've added a new Advanced Guide on how to use custom strings as challenges, including during verification. It captures the learnings from this issue in a way that should hopefully help clear this up for others who have similar use cases as yours:
https://simplewebauthn.dev/docs/advanced/server/custom-challenges#use-a-custom-string-for-challenge
from simplewebauthn.
Related Issues (20)
- generateRegistrationOptions's `excludeCredentials` is returning empty string for each id HOT 1
- extractStrings is not a function HOT 3
- `pubKeyCredParams` field not properly functioning w/ non ES256 Algos on register HOT 2
- Unable to Register a Passkey Using a PIN Code HOT 2
- RFC: Rename @simplewebauthn/typescript-types to something shorter HOT 3
- Treat all custom string challenges as UTF-8 strings
- Uint8Array --> String --> Uint8Array Conversion from isoUint8Array helper doesn't work HOT 3
- Cannot find module 'cbor-x/index-no-eval' or its corresponding type declarations. HOT 9
- 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
- Remove footgun related to random user IDs HOT 16
- 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
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.