تماشای این درس نیاز به اشتراک حرفه‌ای دارد.

Upgrade to WebAuthn v50:00

Alright, remember when I confidently said this? Well, as of recording we're using 4.8 and soon, I imagine, 5.0 will be available. So in order to future-proof this series, future-proof this series, future-proof this series... Well, turns out that literally a couple of weeks after we finished the series, they tagged v5 of that library and essentially a number of things broke. Now, it isn't a massive deal to upgrade but it can make things more complicated, especially seeing as we're already dealing with a complex subject here. So, here's what I recommend. Go and actually use v4 of the WebAuthn framework library for this series and then come back to this episode where we'll upgrade together. I'm going to show you exactly what you need to do to move over to v5. Let's get started. Let's make a start in composer.json. We're going to bump WebAuthnLib to greater than 5.0 and these four packages that we installed manually in v4.

Update Composer Dependencies1:00

to move over to v5. Let's get started. Let's make a start in composer.json. We're going to bump WebAuthnLib to greater than 5.0 and these four packages that we installed manually in v4 are now sub-dependencies of WebAuthnLib so we can actually remove them entirely. Let's then go to the terminal and run composer update to make sure we have all the latest goodies, which is wonderful but of course, once you've updated to v5 and you go back to the browser and refresh, you no longer see that passkey pop-up. So, something's going wrong. To see what's going on, I'm going to pop herd up on the right-hand side and take a look at the logs that are taking place. So, you can see we have this log here. It's trying to set the content of the response as a string but it's been given a public key credential request options object instead. Let's try and figure out what's taking place. Let's open up our API passkey controller and

Fix JSON Serialization1:49

as a string but it's been given a public key credential request options object instead. Let's try and figure out what's taking place. Let's open up our ApiPasskeyController and in authenticateOptions, we do build an instance of the PublicKeyCredentialRequestOptions and then we return that object at the bottom of the method here. Now, in previous versions of the WebAuthnLibrary, this automatically converted to a JSON string because it implemented the JsonSerializable method but that's no longer the case in v5. We have to do that step manually. Now, we don't have to worry about the ins and outs of that because there is already a solution provided. If you go to our StandardPasskeyController, remember this class here, the WebAuthnSerializerFactory? Well, that has a deserialized method but it also has a serialized method. Now, I think we're using this class a lot now.

Create JSONSerializer Helper2:35

remember this class here, the WebAuthnSerializerFactory? Well, that has a deserialized method but it also has a serialized method. Now, I think we're using this class a lot now. So, what I'm actually going to do is create a helper class. Let's grab this code and then we'll create a new directory inside app. Let's call it support and we'll create a new class in here called JSONSerializer. Make sure I spelled that right. Let's create a static function, public static function called deserialize and we're going to just return the result of the code we've copied from our controller. Okay. So, we build up an instance of the WebAuthnSerializerFactory and then we need to provide two parameters, the answer and the object that we actually want to transform into. So, we'll say string JSON which is what we're essentially passing in and then we'll also pass in a fully qualified class name, a class string

that we actually want to transform into. So, we'll say string JSON which is what we're essentially passing in and then we'll also pass in a fully qualified class name, a class string which will be what we want to transform into and then we'll replace these two parameters here with JSON and into like so. Okay. Whilst we're here, let's also create a public static function called serialize which will do the opposite. We can copy the majority of this off the bat and then we'll chain on the serialize method where we'll need to pass in the data we want to serialize and we'll pass the string JSON because that's the format we want to use and then of course, we would pass in that data object and we would expect to return a string. All right. Let's try that out by refactoring our controller. Instead of manually creating this serializer factory now, we should be able to say JSONSerializer deserialize the answer into a public key credential

that out by refactoring our controller. Instead of manually creating this serializer factory now, we should be able to say JSONSerializer deserialize the answer into a publicKeyCredential instance. Yeah, I think that's so much cleaner. In fact, why don't we go back into the serializer and let's search for instances of the WebAuthnSerializerFactory. We might as well clean this up whilst we're here. So, in passkey, in the data attribute, we are manually creating a publicKeyCredentialSource. We should now be able to say JSONSerializer deserialize the value into a publicKeyCredentialSource object and we've already said that we can no longer automatically JSON encode the values, but we do now have the serialize method that we've just created. So, at the same time, we'll refactor that to use that serialize method inside our attribute. Let's search for WebAuthnSerializerFactory again and looks like we have something inside our

created. So, at the same time, we'll refactor that to use that serialize method inside our attribute. Let's search for WebAuthnSerializerFactory again and looks like we have something inside our PasskeyController here. Hmm, that's just making use of it. What about a bit further down inside our store method? Yeah, here we are. We create a publicKeyCredential. So, once again, we can replace this with JSONSerializer deserialize the passkey to an instance of publicKeyCredential. Let's search again. Yep, I think everything else is literally just an import statement, which we can fix using pint. No need to remove any of that manually. So, now we can make use of our JSONSerializer to fix the initial problem. If we go to our APIPasskeyController, instead of directly returning options, which is failing, we can say JSONSerializer serialize the options that we pass in, and that will go through the WebAuthnSerializerFactory in order to actually

of directly returning options, which is failing, we can say JSONSerializer serialize the options that we pass in, and that will go through the WebAuthnSerializerFactory in order to actually convert to a JSON string. We should be able to do the same for the method up here to register. So, instead of options, we'll say JSONSerializer serialize options, and I think that's now all instances of JSONSerialization and deserialization covered. You know, whilst we're here, there's actually a pretty cool thing I just want to show you quickly. See at the minute, if I want autocompletion on publicKeyCredential, I have to have this comment in place. If I remove this comment, I don't get any autocomplete. Well, now that we've refactored to a deserialized method, I think we can actually fix that with generics. So, what I'll do above the deserialized method is I'll add a doc block. I'm not interested in JSON, that is a standard string, but this

I think we can actually fix that with generics. So, what I'll do above the deserialized method is I'll add a doc block. I'm not interested in JSON, that is a standard string, but this into parameter is actually a class string. That is, it's an instance of a fully qualified class name. And in phpStan, we can use the less than greater than symbols to actually state that it's a certain type. We have to define that type using generics. So, let's say at template to create a new generic template. We'll call it treturn, although it doesn't really matter what you call it. And we're going to say that this class string is basically an instance of treturn. It doesn't really mean anything at the moment, but essentially we're storing a variable inside phpStan or inside phpStorm. And then when we use the return tag, we're going to say that we are returning an object of type treturn. So, in other words, phpStan or phpStorm can use this information.

phpStan or inside phpStorm. And then when we use the return tag, we're going to say that we are returning an object of type treturn. So, in other words, phpStan or phpStorm can use this information to say, what are you a fully qualified class name of? And then once it's worked that out, it will say, well, I know that you are returning an instance of that fully qualified class name, an object that is that fully qualified class name, which means that even without the comments now in an IDE like phpStorm, we get full auto-complete, which I think is really cool. So, there you go, just a little side tidbit for you that can help you clean up the code. And in fact, if we were to come back up to our other instance here, yeah, we can remove this comment as well. Very nice. Sorry, that was a tangent. Let's get back on track. If we now come back to the browser and refresh, yes, we once again get our pop-up to select a passkey. If we select a passkey, well, we're

Update Validators for v58:21

Sorry, that was a tangent. Let's get back on track. If we now come back to the browser and refresh, yes, we once again get our pop-up to select a passkey. If we select a passkey, well, we're not getting any further. This passkey is not valid. Now, if we go back to our PasskeyController, here's the authenticate method. Note that we have this try-catch block. I'm going to comment out the try-catch because essentially that's what's covering the real problem. And then we'll refresh once more and we'll hit testedexample.com. Here we go. Look, too few arguments to function create, zero passed, exactly one expected. So, if we come back, yeah, see this little create check here. We need to make sure we now manually pass a CeremonyStepManager in. No issues. There's a simple way to do that using V5. Let's create a new instance of the CeremonyStepManagerFactory. And seeing as this is authentication, we're looking for the request ceremony to pass in.

simple way to do that using V5. Let's create a new instance of the CeremonyStepManagerFactory. And seeing as this is authentication, we're looking for the request ceremony to pass in. And then note that when we call the check method, we have some red squiggly lines here. That's because a couple of name parameters have been renamed. So, this is now publicKeyCredentialSource and request is now host. And when we actually fix that, you'll see that all of the issues disappear. So, with that change, let's try once again. I'm going to come back to the login screen. We'll select this email address and look at that. We are authenticated. We have successfully logged in using a passkey. Of course, I want to make sure I actually put this try-catch back in place. But yeah, that's login taken care of. Wasn't too difficult. However, I imagine if we go to our profile and if we attempt to add a new passkey, let's say, yeah, loopDowning's fine. Why

back in place. But yeah, that's login taken care of. Wasn't too difficult. However, I imagine if we go to our profile and if we attempt to add a new passkey, let's say, yeah, loopDowning's fine. Why not? Create passkey, your Chrome profile. Yep. Hit continue. And no, the given passkey is invalid. I think we have the exact same error, just in a different controller endpoint. Back to our PasskeyController then. Let's search for store. And if we scroll down in store, yeah, exactly as I expected, where we have our authenticatorAttestationResponseValidator, we're going to need to, first of all, pass a CeremonyStepManagerFactory. And we'll want to call the creationCeremony since we're now creating a passkey in this endpoint. And then for check, note that it's renamed this parameter to host instead of request. So, let's make sure we fix that. And I think that would actually suffice. So, let's go back and try again. Let's create

Verify Passkey Registration10:49

check, note that it's renamed this parameter to host instead of request. So, let's make sure we fix that. And I think that would actually suffice. So, let's go back and try again. Let's create loopDowning, create passkey, ChromeProfile. We'll store it. Hit continue. And there we go. It has successfully been saved. I'm going to remove test. And then I'm going to log out and try logging in with our passkey. Here we are. And boom, we're logged in. Just a few minutes, we were able to upgrade to V5 of the WebAuthn package. Let's just go over exactly what you need to change once more so that we have it in our minds. First of all, remember there is no automatic conversion to JSON anymore. So, you'll need to implement your own version of that. You don't have to build it from scratch. Just use the WebAuthnSerializerFactory to do so. If you go to your PasskeyController, make sure that you're passing a CeremonyStepManager

You don't have to build it from scratch. Just use the WebAuthn serializer factory to do so. If you go to your PasskeyController, make sure that you're passing a ceremony step manager factory to your validators and make sure that you've updated any name parameters to match the new standard and specification. And once you've made those changes, everything should be back to working. I really hope that's helped you overcome a few hurdles so that you can get back to building amazing experiences using Laravel. All right, I'll catch you in the next one. Bye.

دوست دارید گاهی خبرهای Laracasts را ایمیل کنیم؟