Let's Learn Passwordless Auth!
Passwordless auth is an exciting and convenient way to let your users access your apps. In this episode, Maricris Bonzo will teach us how to use Magic Labs to enable it for your apps!
Links & Resources
Click to expand the full transcript
Captions provided by White Coat Captioning (https://whitecoatcaptioning.com/). Communication Access Realtime Translation (CART) is provided in order to facilitate communication accessibility and may not be a totally verbatim record of the proceedings.
JASON: Hello, everyone, and welcome to another episode of Learn With Jason. Today on the show, we have Maricris. Thank you so much for joining us. How are you doing?
MARICRIS: Hi. Good. Thank you for having me.
JASON: Yeah, I'm super excited to have you on the show. I think this is gonna be a lot of fun. We've worked together on a couple of things in the past. I was on your show, so here we are, the shoe's on the other foot, and I will try to do as good a job for you as you did for me when I was on your show. So, for folx who aren't familiar with your work, do you want to give us a little bit of a background on who you are, what you do, all those good things?
MARICRIS: Yeah, for sure. Yeah, so, by day, I am ‑‑ well, first of all, my name is Maricris Bonzo, and by day, I'm advocating for developers at Magic, so, I'm pretty much adding a lot of Magic authentication to a lot of Jamstack, No‑Code and blockchain apps. By night, I'm building an autonomous organization for women in Web3. I'm spending more time learning about DAOs and learning how to build it with women in the Web3 community. I'm passionate about building community and coding. So, I'm excited to be here.
JASON: Yeah, yeah, excited to have you. What's up, Adrian? Thanks for the sub. Two‑month streak. Thank you. All right. So, today, we're going to be talking about Magic and specifically passwordless auth, which when I hear that said out loud, it kind of sounds like an oxymoron, because how can it be secure if there's no password, right? So, what's changed? Like what's different in this setup?
MARICRIS: Yeah, that's a good question. So, the difference is ‑‑ so, with regular, you know, the mainstream way of authenticating to apps is you need a username or an email address and a password.
MARICRIS: And what usually happens is that password is stored in, you know, the developer's server, and so you can only imagine if that developer was, you know, for example, Facebook, they pretty much have, like, a honey pot of passwords and user information that is prone to hacker attack. So, that's, like, the security issue with passwords, and so that's why moving forward ‑‑ or actually now, passwords are becoming more obsolete, and companies are ‑‑ the tech industry ‑‑
You hackers, you dirty hackers.
JASON: Oh, my goodness. That was so loud. Why is it so loud today? Okay. Sorry about that.
MARICRIS: Yeah, dirty hackers. But, yeah, so, the industry's moving towards a passwordless future, which means, you know, it can mean a variety of things, but with Magic, we pretty much have ‑‑ well, we actually offer a variety of login methods, but the most popular one is logging in with your email address only, and then, you know, you get a link sent to your email address asking you to authenticate, and so you click on the link and then you are authenticated into the app. So, that's pretty much what ‑‑ one of the ways to do passwordless authentication, but that is more secure.
JASON: So, the difference here is in a more traditional username and setup, you'd be expecting the company to do all the security things because they're holding on to the passwords and they're holding all the user information in one place.
JASON: And in a passwordless situation, what you're doing is you're letting the user control their own security, because if you keep your own device secure then somebody can't, like, hack the company and get your information, they could only get your email address. They wouldn't be able to gain access to your account unless they were able to also gain control of your email.
MARICRIS: Yeah, that's ‑‑ there's ‑‑ that's pretty much correct. There's a lot to unpack there when it comes to phishing attacks, but that's generally the right idea.
JASON: Gotcha. Okay. Cool. And thinking about this now, I've used these. Like I was thinking, I don't think I've ever used a passwordless auth, but that's actually not the case at all. I've gotten ‑‑ I've seen a handful of different things where you request a login and it emails you, you click the link and you're logged in. I've also seen it where you log in by, like, proving that you own your phone. So, you have to enter a thing. If you log in with a CLI and it takes you to the app and says, hey, did you mean to give this CLI permission and you say yes and the CLI is logged in with no passwords. There are a lot of ways you can do passwordless auth that I wasn't thinking about. I guess this is more common than I thought. (Laughter)
JASON: Well, cool. So, this is ‑‑ and it's also just convenient, right? Like I think one of the ‑‑ so, it gets easier with password managers. I use 1Password. It usually means when I go to a site, I don't have to remember a password, I click the 1Password button and it's like, great, I got you and logs in for me. When I'm not on my computer, it's such a pain because I use a different password for every app. It's not a password that is human readable. They're generated long strings. I've got my phone open and looking at this horrifying string and typing it one character at a time. I always true it up.
MARICRIS: Oh, no!
JASON: So, that part of passwords, I appreciate the security of it, but it's really a pain when you get off your own machine.
JASON: Whereas with passwordless, if I've got my phone or if I've got my email account that I can get into, then I can log in on any machine without having to, like, hen peck one character out of a 35‑character random hash, right?
MARICRIS: Yeah, exactly. Mm‑hmm.
JASON: Well, that's cool. So, that is, I think, that's interesting. So, how ‑‑ I guess how does Magic approach this? Because what you're, like, I guess what I'm thinking is, you know, we're talking about ‑‑ there's still a password, right? Like the password is I have access to my email. Instead of being a string. So, what happens or what changes in the way that you think about building apps when you're using this passwordless flow? Where does the control go? Like instead of trusting the centralized app, you're trusting the user to manage their own security. Where does Magic come in and how does trust factor in there?
MARICRIS: Yeah, that's a really good question. So, to answer your question, the control does go to the user.
MARICRIS: And the way Magic is really more magical than other authentication collusions is that when a user logs in, what Magic gives them instead of having, like, a password, Magic gives them, generates a public and private key pair, and that is what is used to authenticate them to the app in future sessions. So, the passwords are actually ‑‑ another differentiator with Magic is that we're not just, like, authentication solution. We are also providing, like, a wallet for users to store their public and private keys, which means we have a key management solution where we store ‑‑
MARICRIS: ‑‑ those keys. So, we have a non‑custodial, like, delegated key management solution to that.
JASON: Okay. Okay. I mostly follow. We're starting to get into territory that I know nothing about, so, I'm going to be leaning on you heavily to explain what things are.
JASON: So, when we talk about this, actually, you know what? It might be easier if we just look at it, because I think I'm gonna start asking questions that are pretty abstract here, so if you don't mind, I'm gonna switch us over into pair programming view.
JASON: And let's start by giving a shout‑out to our Closed Captioning. We've got Jordan here with us doing live captioning from White Coat Captioning. White Coat's here every show help making this show accessible. That is made possible through our sponsors, Netlify, Fauna and Auth0, all kicking in to make this show more accessible, which I really appreciate. We are on with Maricris, who is @seemcat on Twitter. You can find a link to go and follow her. Make sure you do that. And we're looking at ‑‑ it's magic.link, right?
JASON: Okay. So, this is the service that we're using today, is magic.link. You can go and check it out there. If I want to get started, where do I start? Do I build an app first? Do I start with Magic? What's the flow here?
MARICRIS: Usually ‑‑ well, as a developer myself, I would go into the docs and understand, you know, how the technology works, but once you get a feel for that, then you can go to log in ‑‑ or get started, actually.
JASON: Get started. Here I go.
MARICRIS: Yeah, to the upper‑right. And then you can sign up for your free trial.
JASON: Okay. Here I go. All right.
MARICRIS: This is, like, our technology. So, you're using Magic to be authenticated.
JASON: Okay. So, I just got an email that I will show everyone here that says, "click the button to log in," right? And so I'm gonna click that. And ‑‑ okay. All right. So now both of them are here on this tab, so I'll close one of them, and I'm now in.
MARICRIS: So, now the question I want to ask is ‑‑ actually, I think it would be fun for us to do, like, integrate Magic with an an 11ity template.
JASON: Yeah, we can do that.
MARICRIS: I would suggest using Stephanie Eckles because that's the one I've used as well. If you go to her tutorial, create an 11ty site from scratch on Egghead. If you start watching, it will give you a source code on GitHub. We want to get the last lesson source code.
JASON: The last lesson source code. Okay. So, this one down here?
MARICRIS: Yeah, we want to get from this one.
JASON: Got it. All right. Let me ‑‑ let's see. Can I ‑‑ how does one get just ‑‑ here's what I'm gonna do. I'm gonna clone ‑‑ let's make a directory called ‑‑ we're gonna call this magic‑11ty‑passwordless‑auth, and I'm gonna move into it. And inside here, I'm gonna clone ‑‑
Oh, so, we would need to add some code.
JASON: We do need to add some code. Let's get this in here, and what I'm gonna do is copy the lessons ‑‑ where am I? Let's copy ‑‑ oops. Copy 11ty lessons and it was lesson 5. We're gonna not CD, copy it. Let's do one of these so that I don't miss anything. So, now there is our 11ty external data. I'm going to remove the 11ty Egghead collection. I should have copied everything inside of it, not ‑‑
MARICRIS: Yeah, cool.
JASON: Okay. Let's try that one more time. So, we're going to copy everything right here. There it is. Okay. So, I'm going to remove this 05 and I'm gonna npm install. And I think ‑‑ did I miss? I missed this 11ty.js. So, let me just grab this whole file.
MARICRIS: Your terminal game is strong. (Laughter)
JASON: I have the GitHub CLI makes a lot of this stuff just so much more approachable.
JASON: Because you only have to know the names of things, which is really nice. So, let me drop this in. And then I can come back here. Let's copy this again. And now what I want to do once this is done... and let me drop a link to this 11ty site from scratch because that's also ‑‑ that's a good one. We should make sure that shows up in the notes.
JASON: Okay. Are you done or are you doing things?
MARICRIS: It looks like it's still typing. Oh, it says "completed," interesting.
JASON: I think it might just be ‑‑ it's doing something in between downloading rxjs here. If this done change in a second, I might stop it and try again. Hello? Okay. I'm gonna stop it.
JASON: All right. So, let me open this up, and inside we have Node modules. We've got all of this. Let me create this .eleventy.js and we'll put the content of this file in there. Okay. Now just try to npm install one more time and see how this goes. Is it just a really big file or is something going on here? I don't know enough about RXJS to know ‑‑
JASON: Wait, why are we even getting RXJS? It must because because of that, I guess.
MARICRIS: I'm not sure. If that doesn't work, do you want to just ‑‑ I'm not sure how these sessions work, but is it okay if you just install the GitHub repo of the code that I've written and maybe we can just start from scratch for each of the pages?
JASON: Yeah, sure. We can totally do that.
MARICRIS: Okay. Let me send to you per email.
MARICRIS: This is actually, like, maybe the second ‑‑ actually, the first coding session I've done livestream.
JASON: The live stuff is, I mean, you know, the blessing and the curse of livestream is it's fun to see how the getting started experience works. And also you get to see how the getting started experience works. (Laughter) Things are gonna go wrong. Things are gonna go weird. And that's what makes this fun. If there was no chaos, why are we even here?
MARICRIS: That's true. That's true.
JASON: However ‑‑
MARICRIS: Just sent it to your email.
JASON: Oh, what's up? We got ‑‑ I see Ben in the chat. I see Brennan in the chat. What's up, everybody? Welcome. Welcome. Thanks for hanging out. Still don't know what's going on with this, so I think we're just gonna give up on that one and I am going to get ‑‑ where ‑‑ I don't ‑‑ you emailed it to me?
MARICRIS: Yeah, I emailed it to you.
JASON: Okay. I'm looking. Let me send it again. ‑‑
MARICRIS: Let me send it again.
JASON: Thanks for the host, nettings. Hey, clue. Thank you very much. Let's see. All right. I might have to go and find you on GitHub so that we can do this. What's your GitHub?
MARICRIS: It's seemcat, s‑e‑e‑m‑c‑a‑t.
JASON: Okay. And is the repo going to be toward the top here?
MARICRIS: I think it might be. It's called Magic 11ty, if you want to search for it.
JASON: Spelled eleventy or number 11ty?
MARICRIS: Number 11ty.
JASON: We're going to get this one and remove that wasn't doing what we want, and instead, we're going to GitHub clone this one ‑‑ oh, GitHub repo clone this one. Okay. So, let's get that. I'm going to npm install. I'm now wondering ‑‑ there was a little bit of an outage today on U.S. east 1. So, if this also hangs on us. No, that one just worked. I don't know what was going on. All right. So, something was goofy in that repo or my computer hung up on it, which is probably the more likely case. So, this one is doing exactly what we want and we ‑‑
JASON: ‑‑ have ourselves a repo. Let me close this one out so I don't end up accidentally backing it. So, in here, we've got a handful of things going on. And let's take a look at ‑‑ we've got a login. We've got our profile.
JASON: All right. What should I be ‑‑ what should I be looking at here? Like where should we start?
MARICRIS: Yeah, so, let's ‑‑ let's start by deleting some pages so that ‑‑ or folders so that we can restart them. So, let's go into the public folder and maybe just go into the login page.
MARICRIS: And, yes, go in there, and maybe just go down real quick. Yeah, let's just delete all that that's in there.
JASON: Like everything in the body?
MARICRIS: Yeah, everything in the body. Yeah. All the script tags, actually. We can delete.
JASON: Okay. So, all the script tags are gone.
JASON: Should I keep the form so that we don't have to ‑‑ I guess we can get rid of this, but that way we don't have to write the form itself. We can just kind of keep this here.
MARICRIS: Yeah, that's a good idea.
MARICRIS: And we can do the same for logout, too.
JASON: This whole ‑‑ wait, did I just do something ‑‑
MARICRIS: Oh, I think you went into a different ‑‑
JASON: Okay. I was just looking to see, make sure ‑‑ I just got myself confused.
MARICRIS: Oh, okay. And then let's go into the logout HTML file.
JASON: I think I'm ‑‑ okay, so here's what I'm confused about. I think11ty's gonna generate these files. I think we need to be deleting in here. So, I'm gonna delete here and then I have a suspicion that this layout is gonna be what has all of the, yeah, here is all of the Magic stuff. So I can clean all of this out. All right. And then in here, that all looks right. Ignore that for now. So, now we've got the ‑‑ the login has a form. That pulls from our header.njk, which is now empty. Do I need to clear anything else out?
MARICRIS: The profile page. The profile ‑‑ let's clear out the script tags.
JASON: Gone. Get them out of here.
MARICRIS: Yeah, all right. Okay. So ‑‑
JASON: Look at Co‑pilot trying to write this for us.
MARICRIS: I know. Honestly, though, let's use that later. I'm just kidding. So, yeah, first thing you do now that you got your template set up is we gotta set up Magic, right?
MARICRIS: Well, actually, before that you set up the pages, which we already have. The simple pages, the login, profile and homepage.
JASON: If I run this, we should be able to see it and they'll just be empty pages.
MARICRIS: Good idea.
JASON: There we go. Log in to see my blog. We've got login, okay. And then we've got profile. There it is. Okay. So, there is a simple site. It's got all the things we need. And now I'm ready. Let's do it.
MARICRIS: Yeah, let's do it. So, we will start by setting up Magic.
MARICRIS: So, let's go into the header file, header.njk file.
MARICRIS: Just give me a second.
JASON: And is this, like, because I know that some of it came from CDN and stuff, right? So, is there a getting started page that's gonna give me all this stuff when I set up? Where would somebody else be if they were kind of following down this path, right? Is it the docs, maybe?
MARICRIS: It would be in the guides. We have a tutorial that I'm helping ‑‑ I'm using as a reference.
JASON: Got it.
MARICRIS: So, if you guy into the guides on the left‑hand side, all the way up. If you scroll all the way up, there's a guides.
JASON: Guides. Got it.
MARICRIS: And then if you look for 11ty, you'll be able to find the tutorial that we are following right now.
JASON: Got it. I'm just thinking I'm gonna have to copy/paste some SDK stuff, so, I want to make sure I've got that.
MARICRIS: Yeah, for sure.
JASON: Okay, so, in the login, where was the header bit?
MARICRIS: Yeah, so, we're gonna set up the header. We're in the section setup header. So, you can just copy this Magic SDK script.
MARICRIS: And then initialize Magic with that. So, what we're gonna do now is ‑‑ so, it's asking for our publishable API key. So, we're gonna get that from our dashboard.
JASON: And I think I saw this right here. So, here's my publishable one. Now, this one doesn't need to be kept secret.
JASON: Because it's going to go into source code, which means anybody can find it, it's available and what's published on the web. I also feel like I should install a nunjucks so we get a syntax highlighting here because that's kind of hard to read.
MARICRIS: Yeah, that's a good idea.
JASON: That's working. Much better.
MARICRIS: Okay. So, that's, honestly, all you need to do to set up. All you need to set up to incorporate a logging in method. So, if you log in ‑‑ no, I guess the pages have been set up already, but that's all we need to set up with Magic. And if you log in now, it should be able to work.
JASON: We have to make it, like ‑‑
MARICRIS: Oh ‑‑
JASON: Click, right? (Laughter)
MARICRIS: I'm jumping the gun. (Laughter) Yes, yes, you do. Yes, we have to make it click. So, yeah, now we're done with initializing the Magic SDK. Let's go ‑‑ let's stay in the login page.
JASON: Stay in the login page.
MARICRIS: And start adding some stuff in there.
MARICRIS: So, in the login page, this is where we're gonna be using the Magic SDK. So, all we need to do ‑‑ we're already, you know ‑‑ the Magic SDK is already accessible on all the files.
MARICRIS: So let's first grab the elements of the CLI. So, let's make sure that we have the form, the input, and the result.
JASON: Oh, Co‑pilot. (Laughter)
MARICRIS: Who's complaining?
JASON: I love it. It's so nice.
MARICRIS: I know. Okay. So, now we want to ‑‑ when the user passes in their email, we want to call a function. So, let's make sure we're listening to that on submit.
JASON: What have you done, Co‑pilot? Is it perfect? Is it already done?
MARICRIS: Not ‑‑ no, not really.
JASON: Okay. All right.
MARICRIS: We can change it.
JASON: Okay. So, here's what we can do. We'll do a handleLogin function and that's gonna taken an event. And for now, we can say, like, todo. Down here we can say form.addEventlistener. That's what we want. And also going to preventDefault here so that it doesn't actually submit. All right.
MARICRIS: Right. And now we need to grab the user's email that they added. So, let's store that in a variable. We also ‑‑ cool. And, yeah, now, this is the email that we're gonna need to pass into the Magic SDK method called login with Magic link. So, if you access Magic ‑‑
JASON: And is it just like ‑‑
MARICRIS: I think we defined it in the header, that NGK, as, like, lowercase, remember?
JASON: LowercaseMagic, got it.
MARICRIS: Let's call that magic.auth. It's gonna return a promise and we need to wait for the promise.
JASON: Gotcha. Gotcha.
MARICRIS: When we call this, we call this with the email address passed into it.
MARICRIS: And it needs to be within an object. That's what it's expecting.
MARICRIS: And so, yeah, so, what it's gonna return to us is, you know, Magic is gonna authenticate the user and it returns a DID token, which represents, you know, your access into this app.
JASON: Did you say d‑a‑d like dad?
MARICRIS: Yeah, dad token, just kidding. No.
JASON: Dad token. Did you actually ‑‑ what was it? D‑i‑d?
MARICRIS: Yeah, DID. It stands for decentralized identifier token.
JASON: Oh, okay.
MARICRIS: It has to do with how the technology works in the backend, like we pretty much ‑‑ the way the public and private key addresses are created are thanks to the standard DID specification. So, that's what we incorporated into that.
JASON: Got it. And does this return just the token or is it like an object I have to pull the object out of?
MARICRIS: No, it just returns a string of the token. This token has by default a 15‑minute lifespan, but with Magic, you can extend that lifespan to, like, days, if you wanted, because some apps would want that.
MARICRIS: Don't you love how it also gives you the comments?
JASON: I know. At some point, I need to get somebody from the GitHub Co‑pilot project on here just to talk about all the amazing things you can do with this because I've seen people ‑‑ you can write a blog post with it.
JASON: You can put a headline in it and it will write prose. It's surprisingly good.
MARICRIS: Yeah, for sure. I agree. So, now that we have the token, we can just display it into the page, if we wanted.
MARICRIS: But, you know, that's not really necessary, but if you wanted, go ahead and display it. And now, ideally, when the user has been logged in, we want to take them to the profile page. So ‑‑
MARICRIS: ‑‑ let's do that. But we only do that if the token exists. Because sometimes, you know, the user will log in and it will return an invalid ‑‑ it will return nothing. So, we need to make sure that exists first. I do see, like, a minor error.
MARICRIS: Because I didn't explain. So, magic.auth and then .loginwithMagicLink.
JASON: LoginwithMagicLink. That's the thing?
MARICRIS: That's the thing. We can test it, actually.
JASON: I thought I was going to have to wait and do some checking and pulling and stuff. So, I'm pretty excited about this. Let's give it a shot here. So, I am ‑‑ here's my site. Am I in the wrong ‑‑ so, here's my login page. So, I'm gonna log in. Wow, it just did the thing.
JASON: Okay. All right. So, I'm gonna go check my email. And I'm clicking the button. Okay. Okay! All right. So, that's pretty amazing that that just, like, worked on the first try. Very handy.
MARICRIS: Yeah, it is. And so what happened there is, you know, Magic, what you just enabled is the Magic iframe, right? So, when the user logs in, the Magic ‑‑
Holy buckets. Did that work?
MARICRIS: Their, you know, DID token, it's being validated by the Magic iframe, so it prevents, like, attacks, like, phishing attacks because it's more secure than if you had done it in the browser itself.
JASON: Nice. Okay. Adrian is asking this, can it be localized? Can you do it in different languages if you want the iframe to show, you know, Spanish, Portuguese, French, you know, whatever?
MARICRIS: Yeah, we do provide localization. We support, like, 30‑plus languages. If you go ‑‑
JASON: And is that just based on the browser language?
MARICRIS: That's a good question. You're gonna need to specify the locale when initiating Magic, actually.
JASON: Okay. So, if I'm in here ‑‑
JASON: ‑‑ then I would just, like, add some options?
MARICRIS: Yeah, not in there. Oh, actually, yes. Sorry. Yes, in there. You would add a comma and then start a new ‑‑ and then add locale.
JASON: And we could do, like, what, es ‑‑
MARICRIS: Es is good.
JASON: Es will work. Let's do that.
MARICRIS: What is es?
JASON: Es should be Spanish, which is the only language that I can kind of, kind of read. Like my Spanish is bad, but it's at least not non‑existent. Oh, wait. Oh, no. Oh, because we're already ‑‑ we've already tokened. Let me use a different email. Hey, look at that!
MARICRIS: Hey, nice.
JASON: That's beautiful. Okay. So, now let me turn that off so that I actually remember ‑‑ because I won't be able to follow any instructions in Spanish. Cool. Okay. Very cool. All right. All right. So, now that we can log in, I can get to my profile. And in here, what we haven't done yet is we haven't, like, checked to see if somebody is allowed to be here.
MARICRIS: Yeah, we are actually gonna do that ‑‑ oh, yeah, actually, we can do that right now, if you want.
MARICRIS: I think that's a good idea. Yeah, so, right now, regardless of whether or not you log in, you can just add the, you know, endpoint and get there.
MARICRIS: So, with that, we want to go into our header file.
JASON: Into the header we go. All right. I'm here.
MARICRIS: Yes. And what we want to do is we're gonna be manipulating the navigation links in here.
MARICRIS: So, actually, we don't even have a navigation bar yet. Sorry, I forgot. But we're gonna add them in here. So, let's add them as just, like, regular links, if you want.
JASON: So, we can do, like, a nav and then in our nav, we can put in some links. So, we'll have ‑‑
MARICRIS: The homepage, the profile page and the login/logout.
JASON: Okay. There's logout. And we need a profile.
JASON: Okay. Whoops. If I can spell it. Okay. So, now we've got ‑‑ here's our list of links. We can go to each of these. We've got a login, a logout. I think I actually didn't change anything in the logout page, so I think I just saw Magic do something, so I think it did, in fact, log us out.
MARICRIS: Yeah, it did, but we can get to that later.
JASON: But, check it out, I can still get to the profile. So, now we want to fix that. So, you said we want to change what we see based on what our status is?
MARICRIS: Yeah, exactly. So, we're gonna be, you know ‑‑ yeah, so, if we go ‑‑ what page did you want to protect? The profile page, right?
MARICRIS: So, let's go into the profile page.
MARICRIS: And what we want to do in here is, yes, create a script tag and we want to check to make sure that the user is logged in. So, Magic actually provides a method that helps us do that right on the client side. But you can also, you know, implement some of your own logic to check that as well. So, it's called is logged in. We're gonna to check if isLoggedIn is true. That's pretty much all you need to do. If that's true, display the page ‑‑ or actually, maybe we can just check if it's not true.
MARICRIS: Then redirect them to the login page.
JASON: Look at Co‑pilot just knowing all the things.
MARICRIS: I know.
JASON: Okay. So, what should happen here if I'm not logged in is ‑‑ okay, I think my logout ‑‑ my logout button didn't actually do what I want, so, maybe we need to build the logout and then check this?
MARICRIS: Yeah. I think that's a good idea. But we did ‑‑ I forgot to mention that isLoggedIn also returns a promise.
JASON: Oh, I understand. So, async function. Let's go, yeah, checkLogin. And then in here, we can move this up and we'll say, const isLoggedIn. It's gonna equal await and then we'll move this.
MARICRIS: I forgot to mention ‑‑ it's probably obvious, but isLoggedIn just turns true or false.
JASON: Yes. Okay, great. So, then we can sigh isLoggedIn. Loggened in. Kenny Loggins is very happy with that, but using it would put us in the danger zone. I'm unreasonably proud of myself for that. Okay. So, this, then, should do what we want. But I need to console log this to make sure that it's actually giving us ‑‑
MARICRIS: And we also need to call it. I don't know if we're calling it yet.
JASON: What a great idea. (Laughter) Oh, jeez. Okay. All right. So, we've actually called the thing. In the console, Magic auth ‑‑
MARICRIS: I'm sorry, it's not magic.auth, it's magic.user.
JASON: Oh. Okay. Here we go. And out to login we go.
JASON: Love it. All right. So, now if I log in again, off to the email. Here, I'm logging in. Tada! And now we're logged in. And when I refresh, it doesn't bounce me out.
MARICRIS: But that's a great question, and I would love to ask how would that work, you know? Like ‑‑
JASON: So, the way that it works in, like, say, APIs, for example, is I would have a token and I would send that token with a request to a serverless function as a header or as a cookie or something like that. And then I either get redirected, if I'm not authorized with a 401, or I would get sent to the page that I'm allowed to see with the presence of that token. And so, like, on Netlify, we do that with cookie‑based redirects where you can check for the presence of a cookie, and then the ‑‑ if you don't have the cookie, you just automatically get redirected away. If you do have the cookie, then you can go through a serverless function to validate that your cookie is still a valid one and then return the page.
MARICRIS: Oh, wow.
JASON: Or you can also just do it with, like, you know a serverless function to an API call. So, in Magic's case, if I have my token that we generated stored in local storage or something, I could hit Magic's API with that token and say, is this a valid user token? And they could send back yes. And we could redirect to the profile page.
JASON: There's some ways to do that, but, yeah, my guess is that there is probably an API in Magic. It's probably calling that API for us we can hit from a serverless function to either show or not show that profile page.
MARICRIS: Yeah, I'm not sure, but the problem that we're trying to solve here is that when we go offline and we do click, like, logout, we still want to be able to log out, right?
JASON: Yeah, so, okay, if I log out here and I go to profile, it should redirect me. After a moment. I don't think that logout is working. Is it just magic.user.logout?
MARICRIS: I see. Okay. Okay.
MARICRIS: Right. Yeah.
MARICRIS: I see. Okay. Cool. Well, I can follow up with you on what the, you know, is available regarding that.
JASON: Okay. So, now that we've got this, can we show some user‑specific data, like, I'm gonna make the assumption that the only thing we know is their email address.
MARICRIS: Yeah. Yes. So, we have a method called ‑‑ you call magic.user.getmetadata, and the only metadata that Magic creates for the user is, you know, their public address, and that's pretty much mostly it. Yeah. And we also create an issuer. So, let me tell y'all what that is.
MARICRIS: So, the issuer is pretty much the decentralized ID of the user. So, if developers are, like, implementing Magic on their server side, this would be the ID that you would use in your database as your user ID because that never changes. It's unique to every ‑‑
JASON: Got it. Okay.
MARICRIS: ‑‑ person. And then we have public address. I mentioned this before. It's the authenticated users' hub, like, address or public key. Yeah, this is cool because this value is associated with the Ethereum blockchain.
JASON: The Ethereum blockchain?
MARICRIS: Yeah, it's pretty much associated with that.
JASON: So I know exactly zero things about that. Let's see. I typo'd this. Is it metadata with no capital "D?"
MARICRIS: With ‑‑ yeah, with no capital "D".
JASON: Okay. Here's our metadata. So, now we have an email.
JASON: We've got an issuer.
JASON: We've got ‑‑ I haven't enabled multi‑factor auth. I have not said a phone number. And I have a public address. So, okay, great. So, each of these things is something that kind of identifies my user inside of Magic.
JASON: And there's the personally‑identifying information that I've supplied but nothing else. Am I able, like, if I'm an app developer, I want to add, say, somebody's name, can I add it here or do I have to track that separately?
MARICRIS: You would have to track it separately.
JASON: Got it. So, basically, you're providing what I need to say that this person is who they say they are. I'm responsible for tracking any other details that I want.
MARICRIS: That's correct.
JASON: That makes sense. Got it. Okay. Great. So, how ‑‑ so, if I want to use this then, what I can do is I can just break this down and say I want their email, and I think the other thing that's in here is the public address.
JASON: Okay. So, then I can ‑‑ we'll go document.querySelector email and then we can do the same thing for the public address. And we should see both of those things show up.
MARICRIS: Yeah, so, I understand you don't know much about, like, the Ethereum blockchain yet. I actually am wondering when are you gonna fall down that Web3 rabbit hole?
JASON: I'm still doing the watch and listen thing right now. Because I honestly don't have time to actually dig into any of that.
MARICRIS: Yeah, for sure.
MARICRIS: Yeah, for sure. Awesome! So, we have our login page, our profile page, and I guess now let's look at how the logout works.
JASON: Yeah, let's dig in here. So, we can get into ‑‑ oh, I don't even have a logout page. Where did this ‑‑ where did that come from? How did ‑‑ wait, what?
MARICRIS: Oh, is that the thing that we accidentally removed earlier maybe?
JASON: Did we fully remove it?
MARICRIS: I don't think.
JASON: Where ‑‑
MARICRIS: Oh, sorry. No, no, no, it's my fault. (Laughter) Let me hold myself together. There's no page. Sorry. There's only a link and it's gonna be in the header.
JASON: Okay. Great. So, there's no page. There's only a link. Good.
MARICRIS: Yes. So, in here, we're going to use another method of Magic's called logout. It's pretty much called logout. So, what we want to do is add ‑‑ I guess we already have the logout navigation link, and now we want to make sure ‑‑ yes. So, when they click on it, you want to call ‑‑ let's see, magic.user.logout.
JASON: And that's async, right?
MARICRIS: Yes, async again, like all the other methods.
JASON: Okay. So, we're gonna await magic.user.logout, correct?
JASON: Okay. And then we can send somebody to window.href. And let's send them to the homepage, like after you logged out. If I click the logout button, I don't necessarily want to go straight to a logout form.
MARICRIS: That's true.
JASON: So now what we should see here if I click this logout button, it should remove my access. Didn't do the thing. Oh, it didn't do the thing because I didn't reload. Hold on. Nope, it didn't do the thing because I did something wrong.
MARICRIS: Let's see.
JASON: Not read properties of null. Document.querySelector. Window.href. Aha, that's one thing I did wrong. What else? Because it's not handling my ‑‑ cannot read properties of null, reading event listener. You exist. Don't you lie to me. Document.querySelector. Okay. So, let's make this problem smaller. We're gonna get the logoutLink is gonna be that, and then we want this logoutLink.addEventlistener, and in between we'll console.log our logoutLink so that we can verify that it's actually ‑‑ it's not picking it up. Why aren't you picking it up?
MARICRIS: Oh, huh.
JASON: ID = logout. querySelector logout. What have I done here? Does the script need to come after the marker? Oh, smart. Absolutely, yes, it does. We are trying to target something that won't have been rendered into the Dom yet. Good catch. That would have taken me way too long. Because it's not async, and I'm so used to operating in, like, fully async mode that that wouldn't have ‑‑ yeah, okay. Good. Good. Thank you! Thank you, guys. All right. So, no, this is great. Now if I try to go to profile, absolutely not. You can't go there. But I can go to login. And I can log in. And it pops that up. Go back over to my email. Here we go. I'm logged in. Now I can see my profile. I go to "home," I go to "profile," right? And then here's my profile. If I log out, try to go to "profile," absolutely not. We did it!
JASON: This is really exciting. Honestly, this went, even with me not being able to get the dependencies installed and having to take a complete left turn to use a different code base, and even with some side tracking in there, we were able to get this whole thing built way faster than I would have expected. We're not even at an hour yet and we've managed to get this whole thing kind of up and running. So, from here, everything else that happens is building your app, not necessarily working ‑‑ worrying about auth. That's pretty slick. That feels pretty good. So, what else, like, is there anything else that you wanted to show in terms of functionality here within this app or is there something else you want to dig into?
MARICRIS: No, there's pretty much nothing else. I think I was afraid that this was gonna take way too quickly because the template with Magic is super easy. I guess the next challenge would be for, you know, developers to ensure that the navigation link, you know, displays the right links, depending on.
JASON: Yeah. Actually, let's do that.
JASON: So, here's what we can do. Is we don't need that anymore because we know that works. And what we can do is let's go with a function called updateNav. And that's gonna be an async function. And I'm going to ‑‑ let's add ‑‑ is there, like, a hidden ‑‑ let's see. So, we'll just add a hidden class of display: none here, and we're gonna use that to hide some of the things that we know should only be shown when you are authorized.
MARICRIS: I think ‑‑ is ‑‑
JASON: I just need to do it in the CSS, maybe. Where did these colors even come from? Let's take it out of here. Drop it into here. Does this one work? No? Okay. Where do these colors come from? I'll tell you what I can do is I can just drop it right in line.
JASON: So, let's do it like this. Okay. And now, there we go. All right. When in doubt, put it all in the same file. Then in here, in our updateNav function, which I'm going to call before I forget. We ‑‑ can ‑‑ so, how ‑‑ I need to check whether or not they're logged in.
JASON: And we can use the same logic from Magic.
JASON: Which is await magic.user.isLoggedIn. Okay. And then we can say if isLoggedIn, we want to show ‑‑ why don't we make a list of our links here. We've got our profile is gonna be document.querySelector. How do I want to do that? Let's go with ‑‑ let's use one of these. That feels like it's probably fragile, but it's gonna do ‑‑ it's gonna work for now. This is something the CSS attribute selectors, actually, we did a whole session on this with Michael Chan that you should absolutely watch because it was so much fun. Go check that out. Let's see. There's a question here, too. Can we make it redirect to a page when clicking on the Magic link like a callback URL or should we always come back to the original tab? That's a good question. Is there a way to customize the behavior of the login? Can I put in a callback function or anything?
MARICRIS: Yeah, you can definitely put on a callback function and have the user be redirected somewhere else other than, you know, that page. So, if you go ‑‑ you would need to add it in ‑‑ yeah. So, let's, like, look it up real quick.
JASON: I just found, yeah, there's, like, a redirect URI.
MARICRIS: That's what I was looking for. Yeah, you add the key into the login with Magic method.
JASON: Got it, got it, got it. Oh, and then at the URL you redirect to, you need to call login with credential to make sure that it actually finishes the login?
JASON: Got it. Cool. Very cool. All right. And then I see some other things that are kind of exciting here, like log in with SMS. That looks like a fun thing that we probably won't have time to look at today. Is it as easy as the email was?
MARICRIS: Yeah, it is. Definitely.
JASON: I like that nod. Gonna be great. Don't worry. Okay. So, if we've got our profile, then we can get login, and we can get logout.
JASON: And then ‑‑ so, if isLoggedIn, we want profile.classList.add ‑‑ or no, remove hidden and then we want to do the same for logout. But for login, we want to add the class. And then down here, we're gonna do the same thing in reverse. Whoops. Copy. Paste. And theoretically speaking, what will happen is if I've done this correctly, which it looks like I didn't, cannot read properties of null classList, which means it is href login. Oh, logout is not ‑‑ that one's gonna need to be an ID.
JASON: And now we see login. Aha! All right. So, I can log in. I'm gonna log in.
MARICRIS: Yep. That was really clean. I can't wait to see.
JASON: And then click the button. Tada!
JASON: Look at it. All right! So, this is great. This is doing exactly what we want. And this is ‑‑ so, this is the sort of thing that, like, what I like, at no point during this process did I have to try to wrap my head around authentication flows or things like that. So, I Love OAuth 2, I use it all the time. I've got my app, that's going to call callback with a token. That's gonna give me an actual token and I need to save that. You're kind of doing these architecture diagrams in your head, which is fine, it works, and usually there is a code sample you can just copy/paste and be on your way, but what I like here is you've taken a similar flow. You're saying give me your email and then your password is your email.
JASON: I go to my email, I click the thing to say, yes, this is me. You've abstracted that callback site where you get a temporary token and exchange for a real token and you're logged into the app. I get to say logged in, not logged in and continue with my day. That's a really nice flow. Looking at it here, this is pretty sensible code. Like is this logged in? I am logged in. I am logged in. Show me these links and hide these links. Otherwise, show these links and hide the other links. And the same thing here, like, do I want to log out? Great. Hit logout. Do I want to log in? Hit login, right? Like we're in pretty solid shape. This is great. This is ‑‑ it's intuitive. I understand what's going on. It's low impact because I as the user immediately figured out what was going on. I went and I logged in. I never had to reset a password. I didn't have to figure out where I was gonna store that. So, yeah, I dig this. I'm a fan. What's up, Alex and friends? You just missed us building a passwordless auth app. So, why don't we just demo for everybody here everything that we've done. So, I'm gonna log out. Okay. So, we're at home. You can see we've got a home link and the login link. Click "login," and it only asks for my email address. I just realized we should add ‑‑
MARICRIS: Yeah, like ‑‑
JASON: A label.
MARICRIS: Yeah. I do want to ‑‑ I love the whole, like, review you shared on Magic. I love it. Thank you. One, like, little correction is instead of, like, a password, you have public and private key pairs. So, you can think of that as an abstracted version of the passwords.
MARICRIS: Oh, no.
JASON: And it's secure because I'm the only person who can access my email address. So, it's, you know, not something that can be brute forced or anything like that. And if I'm trusting anybody to be good at keeping people out of my account, it's gonna be, like, I use GMail. So, trusting that Google is probably pretty good at not letting people hack their databases, and that's okay with me. I'm okay with that. So, this is all the things that I like about OAuth, using a third‑party service without login, without some of the developer setup I was trying to get if I implemented OAuth, just got to drop in the SDK and I'm off to the races.
MARICRIS: It looks like someone asked is the confirmation email customizable? And it is.
JASON: Nice. If I want to do that, we didn't really look at the dashboard at all, so, maybe you can give us a quick tour of what we're gonna find in here. I'm gonna clear this so we can see more of what's going on. So, you can see that I'm logging in here.
JASON: We've got ‑‑ I got some credits. We've got users. And the users is ‑‑ that's, like, me, not the ‑‑ are these users on my app or users on my account?
MARICRIS: Users on your app.
JASON: Got it. Okay.
MARICRIS: And if you want users to ‑‑ if you want other teammates to join you with this, you know, app, you go to the upper left‑hand corner, my team, and then you can manage that.
JASON: Okay. Got it. All right. So, I see passwordless auth here.
MARICRIS: Yeah, so that is we have a passwordless login form that you can use, and this is where you can enable other authentication methods.
JASON: Nice. Okay. We've got a social login. So, we can even do the OAuth if I want to use Google or whatever. Okay, great. Then you've got a login form that already does the thing.
JASON: That's really nice.
MARICRIS: So, that's login form.
JASON: We can customize all of these things.
JASON: Turn on ‑‑ yep, multi‑factor. Good, good. Branding. Here's the email. So, we can customize this here. This is great. Yeah, this is ‑‑ this is wonderful. Now, there are a couple things here with the email, like, if I want to ‑‑ it looks like I can change the color. So, let's make it like a dark purple. Good. I save. And then I can probably put in a logo. See if I've got one here.
MARICRIS: Nice. You've got a couple.
JASON: There we go. Save it. Okay. So, this ‑‑ so, there's light customization but it's not, like, full I can't get into the HTML and mess with it?
MARICRIS: Yeah, I think for that, you would need to contact us to handle that.
JASON: Got it. Okay. So, that's more of like an enterprise‑y feature. If I'm gonna build my business using Magic, I would contact you and we'll work out the price for me to go in and make it all look the way I want it to look.
MARICRIS: Yeah, I think for now.
JASON: Cool. That makes sense. And then it looks like we can edit some things. So, if I save this, and then if I come back out here and log in again, we should see all those changes I just made.
JASON: So, here's my login. We can see that the color's changed. That's already cool. Then if I come out to my email. Let me get it open. There we go. Okay. So, I didn't read the instructions, and so I needed to upload a square logo, but this is great. Here's my customization. We've got some good stuff going on here. I also need to figure out what I did that just got weird because it didn't look like this in the other ones. But still, it'll work. And away we go. Tada!
JASON: Yeah, this is great. Okay. Well, I think we have seen quite a bit here. We can see how to customize it. We got it up and running really fast.
MARICRIS: Oh, can I share one thing in the settings?
JASON: Absolutely, you can, yes.
MARICRIS: So, as I mentioned before, you can, you know, extend the session of the user being logged in, and you can also ‑‑ you also have access control. So you can control who has access to your app, just in case you have an allow list, a black list. So, that's really cool.
JASON: I was actually just gonna ask this. You can restrict it to only a doe plane. If you're building something for your company, you can say only people with, like, if I was gonna build it in Netlify. An @Netlify.com domain can build this thing?
MARICRIS: Yeah, absolutely.
JASON: So, that's very, very cool. Actually, why don't we let everybody try this. So, let's just deploy this thing, yeah? So, I'm gonna git add everything. I'm going to gh repo fork. Created a fork. Yes, I would. Good, good, good. Okay. So, now that I have this, I can git commit everything and we'll say working passwordless auth. And then we will git push origin. Okay. Then I'm gonna Netlify init. And we're gonna see how this goes because I screwed up my terminal. I broke, like, home brew and a bunch of other stuff. We'll see if this wants to fight me.
JASON: But I have faith. Let's call this passwordless‑auth‑magic. Okay. It's gonna auto detect everything, so we should be in good shape here. I'll skip the Netlify.toml. It did it. Aha! Okay. So, now I can Netlify open. And we're on the page. Here it is building. And we should be live in just a couple of seconds here.
MARICRIS: Have you ever built an authentication solution from scratch for your app?
JASON: An authentication solution from scratch? I have built ‑‑ actually, you know, I did. Yeah, I built, like, a username/password setup and it was awful. No, what did I do? Oh, is something configured that I need to change? Let's see. It is building to output is public. Got it. All right. So, let's fix that. And we're gonna say, build and deploy, edit settings. It is public not site. So, we'll save that. Go back to our deploys. Let's build it again. All right. I think it's gonna pull from cache so it should be even faster this time. Or actually, it might not have cached because it didn't build. It did get the node modules at least. So, that will be faster. It goes faster if you head bob. Okay. But, yeah, I remember back in, like, the early ‑‑ or the late 2000s, I guess, around 2008/2009, I got asked to write an article on security for a really big site, and I wrote up, like, how you could hash passwords and do all these things. And it turns out I had no idea what I was talking about and some, like, crytographers took a look at it and were like this was trash. The article was so bad and they had to take it down and I was humiliated. So, I've been kind of skittish about auth ever since then. With that, we now have a working app. Look at it, everybody. Y'all can go and log in. I'm not gonna look at the dashboard so I don't show anybody's email address, but feel free to go check that out. This is really exciting. I love that we were able to build and deploy an app in just over an hour. I think, you know, minus the time we were talking, that took us 65 minutes‑ish. So, this is ‑‑ this is really, really wonderful. And, yeah, like Matt is saying in the chat, there is so many gotcha's with auth. So, letting somebody else deal with that so that, you know, I assume that Magic has hired experts on security and crytpography and all the things that need to be done right to handle security and authentication properly. I'm not any of those people. Magic would not hire me because I'm not good at it. Why the heck would I build that myself when I can trust experts at other companies to do those things for me? So, yeah, I'm really, really excited about this. So, where should somebody go if they want to learn more? Like what resources do you recommend or where should somebody take a look if they want to go further with Magic and passwordless auth?
MARICRIS: Yeah, for sure, our docs has, like, most of the answers.
MARICRIS: About, you know, the ins and outs of the logging in method works, and definitely, like, in the docs, you can see the guides to the upper‑left‑hand corner. We have, like, a variety of guides integrating with, like, Jamstack Tools, like, Gatsby and other things, and then we also have, like, a blog, you know? And that's linked if you go all the way ‑‑ scroll all the way down. All the way down in the folder.
JASON: Oh, all the way down? Gotcha.
MARICRIS: If you go to "social," we have a blog that tells you a bit more about Magic. If you have any questions at all in integrating with Magic, feel free to join our community. Sorry, this is a lot of switching back and forth. But, yeah, our Discord is right there as well. Yeah, if you want to join, we'd be happy to help.
JASON: There we go. All right, y'all. Well, Maricris, this was so much fun. I learned a ton. I think we are at such a good stopping point. We were able to make some really incredible stuff. Do you have any parting words for everybody? Any words of wisdom? Any kind of, like, final this is why I'm stoked about passwordless stuff that you want to leave everybody with?
MARICRIS: Yeah. The one thing ‑‑ the final thing I want to leave everyone with ‑‑ well, yeah, first of all, I'm so excited about what you built. It was really awesome pair programming with you. And Magic authentication, it does all the logging in and logging out and user management, like, so easily, right, for developers. And then there's also a future‑proof side of things and, you know, if you ever wanted to go into that realm, you can talk about it more later. You can join the Discord server. And, yeah, there's a lot more to Magic than that. That's why it's called Magic. So I think that's all I'll leave y'all with.
JASON: I love it. I think there is a lot to explore in this space. It opens up a lot of possibilities. And the thing that I like about it is that it makes it easier for everybody to just go and learn something. I find that so exciting. I love, love, love that it just lowers the barrier to entry to building an app that has an authenticated section. And what better way to try it, everyone, than to go and do some Dusty Domains? We are running an event right now where we want to just encourage everybody to build something with that domain that you've been sitting on for a while. It's for a good cause. It's gonna go to charity. We've got a ton of companies joining in and matching donations. So, we've got $80,000 going to charity. The charities that we're gonna donate to are these ones here. Each site that you donate right now is worth $400 to charity. So, make sure you max out this donation. We need to get about 200 sites launched in order to make this work. So, why not go give Magic a try and ship something and, you know, make some money for charity doing it? It's a great opportunity to learn something and maybe dust off one of those domains. We've got some really incredible stuff coming out. I'm not gonna pull any of these up. We'll ‑‑ keep an eye on Twitter. We'll do some fun stuff with some of the creators and talking about these sites, but lots and lots and lots of cool things coming in on the showcase. With that, I think we're gonna call this one done. So, this episode, like everybody episode, has been live captioned. We've had Jordan here from White Coat Captioning, and that is made possible through the support of our sponsors, Netlify, Fauna and Auth0, all of whom are kicking in to make this show more accessible to more people. While you're checking out things on the site, make sure you go and look at the schedule. We've got so, so, so much good stuff coming up. We are going to get into UI problems later this week. So, make sure you come and check those out. Ceara Crawshaw is going to be here. We've got, like, automation with n8n and Slinkity which is going to be really exciting. Next and Fauna. Database‑driven Next.js app. If you're a content writer, Stephanie is a freaking genius. That's coming up in February. Mark it on your Google Calendar or follow me on Twitch and make sure you're aware of everything that's going live. With that, we're going to call this one done and cooked. We're going to go find somebody to raid. So, chat, please stay tuned, and we will get that raid off the ground right now. Maricris, thank you so much for spending time with us today. We will see you all next time.
MARICRIS: Thank you.
Closed captioning and more are made possible by our sponsors: