-
-
Notifications
You must be signed in to change notification settings - Fork 157
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Consider: Changing the API from callbacks to interface for storage interactions between library and app #60
Comments
I can extract the
For demo/test-purposes, right? |
I think the minimum number of properties for an assertion validation, per spec, is 3: credential ID, public key, and counter. I was thinking maybe create an interface with just those 3, and let credential store implementers extend from that? Just trying to come up with a way we can cleanly demonstrate different storage options without having to change a bunch of code to switch. |
Do you still think this should be reworked? |
If folks are going to want to plug their own database into this, and not really dig in much deeper than that, it seems like a good idea. Based on what I had to do in the "ActiveDirectory" branch to switch from using the in memory database to on prem AD database, I think it would be easier if doing it again knowing exactly what was required as minimum to meet spec. |
Alright, I hear you. I'm just battling a bit to not use the the lambda because the simplicity. Maybe some kind of wrapper could do the trick?
Need to sit down and write some code to get the feeling. Will probably have time this weekend 👍
|
I have an idea,
|
I'm sorting through this issue right now. We have an aspnet core web app using aspnetcore identity and researching the best way to integrate this auth flow and store the credentials using the identity user manager. Has any more work or thought been put into this? |
Maybe a place to start would be to move from concrete |
As far as I am concerned, that looks like the direction I wanted this to go in. PR's are absolutely welcomed! |
Ok, I probably can't do it today anymore but I'll work on one. |
Not sure if my thoughts are valid. I would prefer that the credential store is not part of the Fido2 core library because this concern is something that is very specific to an application. The Fido2 specification doesn't cover where and how the data should be stored. Therefore I would add a new project like What do you think? |
Yes, those are my exact thoughts.
|
@StillLearnin However: there is no storage in this library. The library currently uses callbacks to allow the consumer (app) to retrieve or store information. See the DemoController for an example of the callbaks: The dicussion earlier in this issue is whether a different api (e.g. forcing the consumer to implement a interface defined by us and then calling that interface) would be more practical than callbacks. Again: There is no storage done by the library. We rely on the consumer app to retrieve and store any user or key related information. edit: I do not think using a generic in the constructor is helpful. The only "User" we define is the one used to transmit WebAuthn/Fido2 spec information about a User: https://github.com/abergs/fido2-net-lib/blob/94057aa13609b1baa15a45e3c2e40913fd0a4858/fido2-net-lib/CredentialCreateOptions.cs#L231 as described by the spec I am still open to and considering if a different API than callbacks would be better, but the case that Alex describes is maybe not the way most consumers will encounter (changing different storage mechanisms for demo-purposes). |
@aseigler one way to accomplish what you originally wanted is perhaps to hide the actual implementation of the callbacks in your own interface? :) Psuedo code: public class Controller {
private IMyCredentialStorage credentials = new ADCredentialsStorage();
//private IMyCredentialStorage credentials = new MemoryCredentialsStorage();
//private IMyCredentialStorage credentials = new AspNetCredentialsStorage();
//private IMyCredentialStorage credentials = new MySqlCredentialsStorage();
//private IMyCredentialStorage credentials = new WhateverCredentialsStorage();
[HttpPost]
[Route("/makeCredential")]
public async Task<JsonResult> MakeCredential([FromBody] AuthenticatorAttestationRawResponse attestationResponse)
{
try
{
// 1. get the options we sent the client
var jsonOptions = HttpContext.Session.GetString("fido2.attestationOptions");
var options = CredentialCreateOptions.FromJson(jsonOptions);
// 2. Create callback so that lib can verify credential id is unique to this user
IsCredentialIdUniqueToUserAsyncDelegate callback = async (IsCredentialIdUniqueToUserParams args) =>
{
// USE YOUR METHOD implementation:
var users = await credentials.GetUsersByCredentialIdAsync(args.CredentialId);
if (users.Count > 0) return false;
return true;
};
// OR EVEN BETTER:
IsCredentialIdUniqueToUserAsyncDelegate callback = credentials.CheckUniqueToUser;
// 2. Verify and make the credentials
var success = await _lib.MakeNewCredentialAsync(attestationResponse, options, callback);
// 3. Store the credentials in db
credentials.AddCredentialToUser(options.User, new YOURStoredCredential
{
Descriptor = new PublicKeyCredentialDescriptor(success.Result.CredentialId),
PublicKey = success.Result.PublicKey,
UserHandle = success.Result.User.Id,
SignatureCounter = success.Result.Counter,
CredType = success.Result.CredType,
RegDate = DateTime.Now,
AaGuid = success.Result.Aaguid
});
// 4. return "ok" to the client
return Json(success);
}
catch (Exception e)
{
return Json(new CredentialMakeResult { Status = "error", ErrorMessage = FormatException(e) });
}
}
} |
@aseigler The edit: This would solve your problem, changing from in-memory to AD without touching the callbacks. |
Just for the record: I'm still leaning towards callback because of simplicity and putting less work on the developer that consumes this library. If they want the flexibility of an interface, they are free to define and use one, but we won't force it upon them. |
@abergs I agree on that - I think that this is the right way to do it. |
Sorry, I wasn't very clear in what I was trying to say. You are obviously correct,
What I was trying to suggest is that the On the generics issue:
From the spec
ProblemGiven the current implementation, how can I extend the user class to include optional fields or add custom "additional user account attributes"? With generics, this would be easy. SuggestionProvide a constructor that accepts a generic |
Thank you for a good follow up. Regarding E.g: What's the scenario where you need to access data from the user object where a custom type would be helpful? The only case I can come up with is in the edit:
If one person is confused by it, I think we should expect others to be confused by it. Maybe documentation or comments would be enough to clarify? |
Let's move the discussion regarding TUser to #88 |
I have a second sample credential store (on prem active directory) nearly ready to test. Currently it is fairly messy to switch the code in the controller out from one credential store to another. Therefore, I would think we should:
The text was updated successfully, but these errors were encountered: