{"id":26748333,"url":"https://github.com/runabol/spring-security-passwordless","last_synced_at":"2025-04-14T22:14:44.279Z","repository":{"id":94886498,"uuid":"119298748","full_name":"runabol/spring-security-passwordless","owner":"runabol","description":"Passwordless authentication example application using Spring Boot and Spring Security","archived":false,"fork":false,"pushed_at":"2018-02-06T01:30:58.000Z","size":151,"stargazers_count":134,"open_issues_count":0,"forks_count":22,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-04-14T22:14:37.851Z","etag":null,"topics":["apache2","java","passwordless-login","springboot","springsecurity"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/runabol.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2018-01-28T21:19:55.000Z","updated_at":"2025-01-17T19:09:45.000Z","dependencies_parsed_at":null,"dependency_job_id":"d0eb42fc-6b0d-4bc0-b229-35ee7282237b","html_url":"https://github.com/runabol/spring-security-passwordless","commit_stats":null,"previous_names":["runabol/spring-security-passwordless"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/runabol%2Fspring-security-passwordless","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/runabol%2Fspring-security-passwordless/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/runabol%2Fspring-security-passwordless/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/runabol%2Fspring-security-passwordless/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/runabol","download_url":"https://codeload.github.com/runabol/spring-security-passwordless/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248968917,"owners_count":21191162,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["apache2","java","passwordless-login","springboot","springsecurity"],"created_at":"2025-03-28T10:17:27.863Z","updated_at":"2025-04-14T22:14:44.262Z","avatar_url":"https://github.com/runabol.png","language":"Java","readme":"# Introduction\n\nWe all have a love/hate relationship with passwords. They protect our most valuable assets but they are so god damn hard to create and remember. \n\nAnd just to make things even harder for us humans, more and more companies are now enforcing two factor authentication (you know, the little phone pincode thing) to make it even more complicated to login to our accounts.\n\nDespite advances in biometric authentication (fingerprint, face recognition etc.), passwords still remain the most ubiqutous form of authentication. \n\nSo what can we do to help our fellow users to access our application in an easier manner but without compromising security?\n\nThis is where passwordless login comes in.\n\nHow does it work? \n\nIf you ever went to a website, realized you forgot your password and then used their \"Forgot Password\" then you know what passwordless login is. \n\nAfter you entered your email address on the Reset Password page you were sent a \"magic\" link with a special code (a.k.a \"token\") embedded in it which provided you with the ability to reset your password. \n\nThat website piggy-backed on your already-password-protected email address to create a secure, one-time-password \"magic\" link to your account. \n\nWell, if we can do all that in a presumably safe way when the user loses his password why can't we do it whenever a user wants to login? Sure we can.\n\nOh, and just in case you're wondering some big name (Slack, Medium.com, Twitter) companies are already using this method of authentication.\n\nAlright, let's get down to business then.  \n\n# The nitty gritty\n\n1. Create a [sign-up/sign-in page](https://github.com/creactiviti/spring-security-passwordless/blob/master/src/main/resources/templates/signin.html). It basically needs only one field: email.\n\n```\n\u003cinput type=\"email\" name=\"email\" class=\"form-control\" placeholder=\"Email address\" required autofocus\u003e\n```\n\n2. Create an [endpoint](https://github.com/creactiviti/spring-security-passwordless/blob/master/src/main/java/com/creactiviti/spring/security/passwordless/web/SigninController.java#L35) to handle the form submission:\n\n```\n  private final TokenStore tokenStore;\n  private final Sender sender;\n\n  @PostMapping(\"/signin\")\n  public String signin (@RequestParam(\"email\") String aEmail) {\n    \n    // verify that the user is in the database.\n    // ...\n    \n    // create a one-time login token\n    String token = tokenStore.create(aEmail);\n    \n    // send the token to the user as a \"magic\" link\n    sender.send(aEmail, token);\n    \n    return \"login_link_sent\";\n  }\n```\n\n3. Create an [endpoint](https://github.com/creactiviti/spring-security-passwordless/blob/master/src/main/java/com/creactiviti/spring/security/passwordless/web/SigninController.java#L48) to authenticate the user based on the \"magic\" link:\n\n```\n  private final Authenticator authenticator;\n\n  @GetMapping(\"/signin/{token}\")\n  public String signin (@RequestParam(\"uid\") String aUid, @PathVariable(\"token\") String aToken) {\n    try {\n      authenticator.authenticate(aUid, aToken);\n      return \"redirect:/\";\n    }\n    catch (BadCredentialsException aBadCredentialsException) {\n      return \"invalid_login_link\";\n    }\n  }\n```\n\nAnd that's about it.\n\n# Securing the \"magic\" link.\n\nThere are few precautions you should take to keep the \"magic\" link as secure as possible:\n\n1. When sending the link to the user communicate to your email server over SSL. \n\n2. Tokens should only be usable once. \n\n3. Tokens should not be easily guessable. Use a good, cryptographically strong random number generator. e.g:\n\n```\n    SecureRandom random = new SecureRandom();\n    byte bytes[] = new byte[TOKEN_BYTE_SIZE];\n    random.nextBytes(bytes);\n    String token = String.valueOf(Hex.encode(bytes));\n```\n     \n4. Tokens should expire after a reasonable amount of time (say 15 minutes). In this example I use an in-memory `TokenStore` implementation backed by a `SelfExpringHashMap` which as its name suggests expires entries after a given amount of time. In a real-world scenario you will most likely use a database to store your generated tokens so your website can run on more than one machine and so these tokens survive a crash. But the principle is the same. You can have a `created_at` field which stamps the time the token was created so you can determine if it expired or not.\n\n\n# Running the demo\n\n1. Clone the repo:\n\n```\ngit clone https://github.com/creactiviti/spring-security-passwordless.git\n```\n\n2. Build\n\n```\nmvn clean spring-boot:run -Dspring.mail.host=\u003cSMTP HOST\u003e -Dspring.mail.username=\u003cSMTP USERNAME\u003e -Dspring.mail.password=\u003cSMTP PASSWORD\u003e -Dpasswordless.email.from=\u003cSENDER EMAIL ADDRESS\u003e\n```\n\n3. Sign-in\n\nGo to [http://localhost:8080/signin](http://localhost:8080/signin)\n\n\n# License\n\nApache License version 2.0.\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frunabol%2Fspring-security-passwordless","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frunabol%2Fspring-security-passwordless","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frunabol%2Fspring-security-passwordless/lists"}