<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Johannes Hinkov</title>
    <description>The latest articles on DEV Community by Johannes Hinkov (@munichdeveloper).</description>
    <link>https://dev.to/munichdeveloper</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F763252%2F2883c723-d811-4f3d-9f66-f4643740f90e.jpeg</url>
      <title>DEV Community: Johannes Hinkov</title>
      <link>https://dev.to/munichdeveloper</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/munichdeveloper"/>
    <language>en</language>
    <item>
      <title>How to login your users without a password (but with a magic.link)</title>
      <dc:creator>Johannes Hinkov</dc:creator>
      <pubDate>Wed, 30 Aug 2023 22:19:20 +0000</pubDate>
      <link>https://dev.to/munichdeveloper/how-to-login-your-users-without-a-password-but-with-a-magiclink-228g</link>
      <guid>https://dev.to/munichdeveloper/how-to-login-your-users-without-a-password-but-with-a-magiclink-228g</guid>
      <description>&lt;p&gt;How do you deal with your passwords?&lt;br&gt;
Do you use the same "secure" password on every website?&lt;br&gt;
Or are you generating a random password each time and store it "securely" in a password manager?&lt;/p&gt;

&lt;p&gt;However you do it, i'm pretty sure you don't have &lt;em&gt;really&lt;/em&gt; secure and different passwords for all your accounts (and remember them in mind at the same time..).&lt;/p&gt;

&lt;p&gt;If you put all your randomly generated passwords into a password manager, you will need to be worried every time they get hacked (spoiler alert: they get hacked pretty often).&lt;/p&gt;

&lt;p&gt;If you don't use a password manager, you inevitably end up using the same password over and over again (because no one loves to remember thousands of passwords..) &lt;/p&gt;

&lt;p&gt;So this is a pretty unpleasant situation if you ask me, right?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AoVhEwBa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1f4prp2e78zt9u09t5j6.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AoVhEwBa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1f4prp2e78zt9u09t5j6.jpeg" alt="Image description" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What can you do as a builder to make your users lifes a little better when it comes to dealing with passwords?&lt;/p&gt;

&lt;p&gt;Thanks to magic links there is a nice solution!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WQTRnrn9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vew74r12a36h0pxo0ikw.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WQTRnrn9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vew74r12a36h0pxo0ikw.gif" alt="Image description" width="480" height="480"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  What are magic links?
&lt;/h2&gt;

&lt;p&gt;If you are like me, you often times find yourself forgetting your passwords to your accounts.&lt;/p&gt;

&lt;p&gt;What i'm doing then (and you probably too) is clicking the 'Forgot your password' link and then you get a link to reset your password.&lt;br&gt;
You click the link, which has superpowers (through a generated token which acts like an One Time Password (OTP) that is attached to the link), enter a new password and boom - You have a new password and are able to login again.&lt;/p&gt;

&lt;p&gt;Now you could say a magic link is nearly the same workflow - with the difference that there is no password and you don't have to click a 'Forgot password' link.&lt;/p&gt;

&lt;p&gt;When you want to login to a service, you just enter your email and that's it. You get a magic link emailed, you click the link and you are in.&lt;/p&gt;

&lt;p&gt;Now you might think this is quite uncommon and maybe you are right - Most of the websites are using conventional login workflows with username and password - but .. the magic.link authentication has a lot of advantages!&lt;/p&gt;
&lt;h2&gt;
  
  
  Advantages of magic links
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Great User Experience&lt;/strong&gt;&lt;br&gt;
No one likes to remember passwords and everyone has an email account.&lt;br&gt;
You will never face the problem that a user doesn't login to your service or website only because he forgot his password and is too lazy to reset it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. You don't need to deal with email infrastructure or verifying emails&lt;/strong&gt;&lt;br&gt;
With the solution that i will show you here, you don't need to setup a smtp service or mailing server.&lt;br&gt;
Further, because the email is being validated implicitly (by your users logging in through a mail they get into their mailbox) you don't need to verify / check the provided email adress explicitly.&lt;br&gt;
You know your user has a working email account that is not full every time he logs in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. You don't need your users to explicitly confirm their email on registration&lt;/strong&gt;&lt;br&gt;
In the traditional user/password auth workflow a user will have to confirm his email adress upon registering.&lt;br&gt;
With the magic.link auth workflow, the confirmation is being done on every login implicitly. So again a better user experience for your customers / users.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. You can register users "on the fly"&lt;/strong&gt;&lt;br&gt;
If you don't need your users to enter additional data like name, adress etc. and the email is all you need, you can even further improve the user experience and create an account on the fly when a user is not already existing on login.&lt;br&gt;
So you don't have to differ between signup and signin.&lt;br&gt;
Also if you really need additional data, you can ask later in the process for your user to specify what you need - in a separate formular.&lt;/p&gt;
&lt;h2&gt;
  
  
  Implementing your own authentication workflow with magic links
&lt;/h2&gt;

&lt;p&gt;So let's finally implement the magic.link authentication in your app.&lt;br&gt;
For this example here i'm going to use &lt;strong&gt;Spring Boot 3&lt;/strong&gt;, &lt;strong&gt;React 18&lt;/strong&gt; and the service from &lt;a href="https://magic.link/"&gt;&lt;strong&gt;https://magic.link&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You will need an account which is free in the basic version (up to 1000 users).&lt;/p&gt;

&lt;p&gt;I have extracted the basic functionality into a library which you can integrate as a maven dependency into your Spring Boot project.&lt;br&gt;
I will also show you a reference implementation that illustrates how exactly the library is being used.&lt;br&gt;
You can use the provided reference implementations for Spring Boot and React as boilerplate / base to bootstrap your own projects!&lt;/p&gt;
&lt;h2&gt;
  
  
  The architecture
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mjHcPObw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ugu29nz617qmdntus275.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mjHcPObw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ugu29nz617qmdntus275.png" alt="Image description" width="800" height="517"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's first have a look at the login flow.&lt;br&gt;
The solution here integrates with a backend that uses JWT for authentication.&lt;br&gt;
There's a high chance you are also already using an implementation in your backend that relies on JWT.&lt;br&gt;
If that's the case, you can simply combine the magic.link auth and integrate the service into your app.&lt;br&gt;
It's also conceivable to only use the magic.link auth and solely rely on the DID token in your backend, but that's another way and a different story.&lt;/p&gt;

&lt;p&gt;So here is how it works:&lt;br&gt;
We will integrate magic.link components into our frontend (how exactly is shown down below).&lt;br&gt;
These components will then make a call to magic.link and initiate a login request, as soon as our user enters his email and hits the login button.&lt;br&gt;
The user then will get a popup with an OTP which he will need in a second to login.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SvwI8GWS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n69sk2euc3j79dxk6d7z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SvwI8GWS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n69sk2euc3j79dxk6d7z.png" alt="Image description" width="651" height="723"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At the same time, an email gets send to the provided email adress with a &lt;em&gt;magic link.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The user enters the provided OTP, if it is correct, he will be logged in &lt;em&gt;in the same window, that initiated the login request.&lt;/em&gt;&lt;br&gt;
The other window can be closed.&lt;/p&gt;

&lt;p&gt;Once the user entered the correct OTP, we receive the DID Token in the frontend and can call our backend.&lt;/p&gt;

&lt;p&gt;The backend is going to &lt;em&gt;validate&lt;/em&gt; the DID token (through calling magic.link API) and then, only if it is valid, create a new JWT and give it back to the caller, our frontend.&lt;/p&gt;

&lt;p&gt;From there, we can simply save our JWT as usual in a cookie and provide it in the header in subsequent calls to the backend. &lt;/p&gt;
&lt;h2&gt;
  
  
  user-service (library)
&lt;/h2&gt;

&lt;p&gt;Let's have a look at the implementations and start with the core, the user service i've created as a small library.&lt;br&gt;
You can simply put it into your project as a maven dependency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;de.munichdeveloper.user&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;user-service&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;1.2.3&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you add that library, it will enrich your application by the following components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A rest controller with a method &lt;strong&gt;&lt;em&gt;signinByMagicToken&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BjIN18VH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mfbigbsi9vu01j9nd365.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BjIN18VH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mfbigbsi9vu01j9nd365.png" alt="Image description" width="800" height="207"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This controller will handle the request from our frontend. It expects the DID Token (decentralized Identifier Token), which we will get from the magic.link service and that will be requested by our frontend. &lt;br&gt;
We'll have a look at that later.&lt;/p&gt;

&lt;p&gt;Here is the code that does the magic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;JwtAuthenticationResponse&lt;/span&gt; &lt;span class="nf"&gt;signinByMagicLink&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;didToken&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;IOException&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;InterruptedException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;DIDToken&lt;/span&gt; &lt;span class="n"&gt;parsedDIDToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DIDTokenHelper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parseAndValidateToken&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;didToken&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;issuer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parsedDIDToken&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getIssuer&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="nc"&gt;UserMetadata&lt;/span&gt; &lt;span class="n"&gt;metadataByIssuer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getMetadataByIssuer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;issuer&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;metadataByIssuer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getData&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getEmail&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;jwt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jwtService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;generateToken&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;JwtAuthenticationResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jwt&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We get the DID token handed over, that we got from the magic.link service.&lt;br&gt;
We then parse the token, which is done by some helper methods.&lt;br&gt;
From the parsed token, we read out the issuer, which is the user that needs to be logged in.&lt;br&gt;
We call the magic.link API to get some metadata for the issuer to get the email of the user.&lt;br&gt;
Now we are almost done, at this point we know everything is okay and the user has provided a valid DID token.&lt;br&gt;
Finally, we create a JWT for the email and return it back.&lt;/p&gt;
&lt;h2&gt;
  
  
  Implementing the Spring Boot 3 backend
&lt;/h2&gt;

&lt;p&gt;As already mentioned, you will need to include the user-service lib as maven dependency here in your backend implementation.&lt;br&gt;
Note that the maven dependency is not published and thus you will need to build it on the same machine, where you are going to build the backend service, so that it resides in your local maven repository.&lt;/p&gt;

&lt;p&gt;The next step is to adapt your SpringBoot Main Class and add a few annotations. &lt;/p&gt;

&lt;p&gt;Here's an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@SpringBootApplication&lt;/span&gt;
&lt;span class="nd"&gt;@EnableJpaRepositories&lt;/span&gt;&lt;span class="o"&gt;({&lt;/span&gt;&lt;span class="s"&gt;"de.munichdeveloper.user"&lt;/span&gt;&lt;span class="o"&gt;})&lt;/span&gt;
&lt;span class="nd"&gt;@EntityScan&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;basePackages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"de.munichdeveloper.user"&lt;/span&gt;&lt;span class="o"&gt;})&lt;/span&gt;
&lt;span class="nd"&gt;@ComponentScan&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;basePackages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"de.munichdeveloper.user"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"de.munichdeveloper.magic"&lt;/span&gt;&lt;span class="o"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SpringBootMagicLinkApp&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;SpringApplication&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SpringBootMagicLinkApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need to tell our Spring Application where it needs to look for further components, entities and repositories.&lt;br&gt;
Otherwise, these components from the lib wouldn't get scanned and thus wouldn't work.&lt;/p&gt;

&lt;p&gt;For the sake of completeness, here are two further configs that are needed in certain cases.&lt;/p&gt;

&lt;p&gt;The first is the CorsConfig, which i am using because in my example application, my frontend and my backend are not hosted on the same server.&lt;/p&gt;

&lt;p&gt;The second is the SecurityConfig, which should be pretty straight forward.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Bean&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;SecurityFilterChain&lt;/span&gt; &lt;span class="nf"&gt;securityFilterChain&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpSecurity&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;http&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cors&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Customizer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withDefaults&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;csrf&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;CsrfConfigurer:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;disable&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;authorizeHttpRequests&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;requestMatchers&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/magic/**"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;permitAll&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;anyRequest&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;authenticated&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sessionManagement&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;manager&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;manager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sessionCreationPolicy&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;STATELESS&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;authenticationProvider&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;authenticationProvider&lt;/span&gt;&lt;span class="o"&gt;()).&lt;/span&gt;&lt;span class="na"&gt;addFilterBefore&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                        &lt;span class="n"&gt;jwtAuthenticationFilter&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;UsernamePasswordAuthenticationFilter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I will not go too deep into these, as they are, as already said, pretty straight forward. If you are not familiar with SecurityConf and CorsConfig in SpringBoot, i recommend you google them or just C&amp;amp;P them (and adapt where needed) ;-)&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing the frontend with React 18
&lt;/h2&gt;

&lt;p&gt;Time to have a look at the react frontend.&lt;/p&gt;

&lt;p&gt;In order to use the magic lib, you have to add it to your package.json with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add magic-sdk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To be able to call the magic.auth API, we need to get our public key from our magic account and put it into the &lt;em&gt;.env&lt;/em&gt; file under the key &lt;em&gt;REACT_APP_PK_KEY&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To do so, get into your dashboard of your magic account and create a new &lt;strong&gt;dedicated app&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It should look similar to this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uGXlycvW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tpiib0j6unyz9cqzhhqj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uGXlycvW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tpiib0j6unyz9cqzhhqj.png" alt="Image description" width="800" height="491"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As the name says, it is a public key, so it's ok that it is exposed to the users browser (and you are allowed to see my PK also .. but you should use your own :-P )&lt;/p&gt;

&lt;p&gt;Now, the interesting part is in the code located in &lt;strong&gt;lib\magic.js&lt;/strong&gt;&lt;br&gt;
The method &lt;strong&gt;loginUser&lt;/strong&gt; is being called in the login form (located in components\Authenticate.js)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;loginUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;did&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;magic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loginWithMagicLink&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;authPromise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;authenticateWithDid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;did&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;authPromise&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parsedJWT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parseJwt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;parsedJWT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;isLoggedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, we call the method &lt;strong&gt;loginWithMagicLink&lt;/strong&gt; from the magic auth library.&lt;/p&gt;

&lt;p&gt;The method blocks and &lt;strong&gt;returns the desired did token&lt;/strong&gt; when finished.&lt;/p&gt;

&lt;p&gt;We then &lt;strong&gt;pass the did token to our backend&lt;/strong&gt; by calling our API.&lt;br&gt;
As already described earlier, here we receive a jwt token and can finally hand it over to a callback method, which will then save the jwt into the user context, where it will be picked up for subsequent calls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;Demo Time!! 🎥 &lt;br&gt;
You can have a look at my current project to see the auth login live in action: &lt;a href="https://documan.onrender.com"&gt;docuMAN&lt;/a&gt;&lt;br&gt;
docuMAN is an &lt;strong&gt;AI journaling App&lt;/strong&gt; which i developed primarily for myself :-)&lt;br&gt;
It helps me to &lt;em&gt;efficiently&lt;/em&gt; document things from work (and also other areas of my live) &lt;em&gt;AND also find them later.&lt;/em&gt;&lt;br&gt;
This last point is crucial, because i was documenting so much stuff (in traditional apps like evernote) only to later struggle with finding the desired informations again.&lt;br&gt;
&lt;strong&gt;docuMAN&lt;/strong&gt; helps here with a mix of AI and elasticsearch, so i can really find what i need.&lt;/p&gt;

&lt;p&gt;If you think this is a cool idea and it could be of use for yourself, i would be happy if you try docuMAN out for free!&lt;br&gt;
Here's the link again: &lt;a href="https://documan.onrender.com"&gt;https://documan.onrender.com&lt;/a&gt;&lt;br&gt;
Any feedback is highly appreciated and helps a lot!&lt;/p&gt;

&lt;h2&gt;
  
  
  Final words
&lt;/h2&gt;

&lt;p&gt;Thank you guys for reading my article!&lt;br&gt;
If you found it useful i would be happy if you star the repos on github and drop a like on this post here! ⭐⭐⭐&lt;/p&gt;

&lt;p&gt;Also feel free to ask me &lt;strong&gt;any questions&lt;/strong&gt; or send any &lt;strong&gt;problems&lt;/strong&gt; you might have!&lt;/p&gt;

&lt;p&gt;All the code is available on github in these repositories here, feel free to use it for your next projects (MIT license):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/munichdeveloper/user-service"&gt;user-service library&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/munichdeveloper/spring-boot-magic-link"&gt;Spring Boot3 backend boilerplate&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/munichdeveloper/react-magic-link"&gt;React18 frontend boilerplate&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, keep in mind this is not &lt;em&gt;production ready&lt;/em&gt; code! You will have to finish the code if you are really going to use it &lt;em&gt;in production&lt;/em&gt; ;-)&lt;/p&gt;

</description>
      <category>magiclink</category>
      <category>javascript</category>
      <category>authentication</category>
      <category>passwordless</category>
    </item>
  </channel>
</rss>
