{"id":28113211,"url":"https://github.com/samdjstevens/java-totp","last_synced_at":"2026-02-13T18:58:05.109Z","repository":{"id":40797245,"uuid":"206662517","full_name":"samdjstevens/java-totp","owner":"samdjstevens","description":"A java library for implementing Time-based One Time Passwords for Multi-Factor Authentication.","archived":false,"fork":false,"pushed_at":"2024-07-30T15:46:23.000Z","size":150,"stargazers_count":504,"open_issues_count":25,"forks_count":122,"subscribers_count":11,"default_branch":"master","last_synced_at":"2025-07-17T13:35:24.546Z","etag":null,"topics":["2fa","authy","google-authenticator","java","mfa","totp-tfa"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/samdjstevens.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,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2019-09-05T21:45:37.000Z","updated_at":"2025-07-17T08:24:38.000Z","dependencies_parsed_at":"2025-05-14T05:06:40.797Z","dependency_job_id":"b5d4cd83-f96d-47cc-960f-862a9407750b","html_url":"https://github.com/samdjstevens/java-totp","commit_stats":null,"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/samdjstevens/java-totp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/samdjstevens%2Fjava-totp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/samdjstevens%2Fjava-totp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/samdjstevens%2Fjava-totp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/samdjstevens%2Fjava-totp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/samdjstevens","download_url":"https://codeload.github.com/samdjstevens/java-totp/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/samdjstevens%2Fjava-totp/sbom","scorecard":{"id":797320,"data":{"date":"2025-08-11","repo":{"name":"github.com/samdjstevens/java-totp","commit":"6a047690fa1d0f88b86d5d1397900f9ed959c044"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.1,"checks":[{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Code-Review","score":3,"reason":"Found 6/20 approved changesets -- score normalized to 3","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 19 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":0,"reason":"14 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-cm59-pr5q-cw85","Warn: Project is vulnerable to: GHSA-rc42-6c7j-7h5r","Warn: Project is vulnerable to: GHSA-xf96-w227-r7c4","Warn: Project is vulnerable to: GHSA-36p3-wjmg-h94x","Warn: Project is vulnerable to: GHSA-hh26-6xwr-ggv7","Warn: Project is vulnerable to: GHSA-4gc7-5j7h-4qph","Warn: Project is vulnerable to: GHSA-4wp7-92pw-q264","Warn: Project is vulnerable to: GHSA-g5mm-vmx4-3rg7","Warn: Project is vulnerable to: GHSA-6gf2-pvqw-37ph","Warn: Project is vulnerable to: GHSA-rfmp-97jj-h8m6","Warn: Project is vulnerable to: GHSA-558x-2xjg-6232","Warn: Project is vulnerable to: GHSA-564r-hj7v-mcr5","Warn: Project is vulnerable to: GHSA-9cmq-m9j5-mvww","Warn: Project is vulnerable to: GHSA-wxqc-pxw9-g2p8"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-23T09:23:09.616Z","repository_id":40797245,"created_at":"2025-08-23T09:23:09.616Z","updated_at":"2025-08-23T09:23:09.616Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29414301,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-13T06:24:03.484Z","status":"ssl_error","status_checked_at":"2026-02-13T06:23:12.830Z","response_time":78,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["2fa","authy","google-authenticator","java","mfa","totp-tfa"],"created_at":"2025-05-14T05:01:33.382Z","updated_at":"2026-02-13T18:58:05.088Z","avatar_url":"https://github.com/samdjstevens.png","language":"Java","readme":"# Time-based One Time Password (MFA) Library for Java\n\n[![CircleCI](https://circleci.com/gh/samdjstevens/java-totp/tree/master.svg?style=svg\u0026circle-token=10b865d8ba6091caba7a73a5a2295bd642ab79d5)](https://circleci.com/gh/samdjstevens/java-totp/tree/master) [![Coverage Status](https://coveralls.io/repos/github/samdjstevens/java-totp/badge.svg)](https://coveralls.io/github/samdjstevens/java-totp) [![Maven Central](https://img.shields.io/maven-central/v/dev.samstevens.totp/totp.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22dev.samstevens.totp%22%20AND%20a:%22totp%22)\n\nA java library to help generate and verify time-based one time passwords for Multi-Factor Authentication.\n\nGenerates QR codes that are recognisable by applications like Google Authenticator, and verify the one time passwords they produce.\n\nInspired by [PHP library for Two Factor Authentication](https://github.com/RobThree/TwoFactorAuth), a similar library for PHP.\n\n## Requirements\n\n- Java 8+\n\n\n\n## Spring Boot\n\nThe quickest way to start using this library in a Spring Boot project is to require the TOTP Spring Boot Starter. See [Using Java-TOTP with Spring Boot](totp-spring-boot-starter/README.md) for more information, or read on to learn about the library.\n\n\n\n## Installation\n\n#### Maven\n\nTo add this library to your java project using Maven, add the following dependency:\n\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003edev.samstevens.totp\u003c/groupId\u003e\n  \u003cartifactId\u003etotp\u003c/artifactId\u003e\n  \u003cversion\u003e1.7.1\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n#### Gradle\n\nTo add the dependency using Gradle, add the following to the build script:\n\n```\ndependencies {\n  compile 'dev.samstevens.totp:totp:1.7.1'\n}\n```\n\n\n\n## Usage\n\n- [Generating secrets](#generating-a-shared-secret)\n- [Generating QR codes](#generating-a-qr-code)\n- [Verifying one time passwords](#verifying-one-time-passwords)\n- [Using different time providers](#using-different-time-providers)\n- [Recovery codes](#recovery-codes)\n\n\n\n### Generating a shared secret\n\nTo generate a secret, use the `dev.samstevens.totp.secret.DefaultSecretGenerator` class.\n```java\nSecretGenerator secretGenerator = new DefaultSecretGenerator();\nString secret = secretGenerator.generate();\n// secret = \"BP26TDZUZ5SVPZJRIHCAUVREO5EWMHHV\"\n```\n\nBy default, this class generates secrets that are 32 characters long, but this number is configurable via\n\nthe class constructor.\n\n```java\n// Generates secrets that are 64 characters long\nSecretGenerator secretGenerator = new DefaultSecretGenerator(64);\n```\n\n\n\n### Generating a QR code\n\nOnce a shared secret has been generated, this must be given to the user so they can add it to an MFA application, such as Google Authenticator. Whilst they could just enter the secret manually, a much better and more common option is to generate a QR code containing the secret (and other information), which can then be scanned by the application.\n\nTo generate such a QR code, first create a `dev.samstevens.totp.qr.QrData` instance with the relevant information.\n\n```java\n QrData data = new QrData.Builder()\n   .label(\"example@example.com\")\n   .secret(secret)\n   .issuer(\"AppName\")\n   .algorithm(HashingAlgorithm.SHA1) // More on this below\n   .digits(6)\n   .period(30)\n   .build();\n```\n\nOnce you have a `QrData` object holding the relevant details, a PNG image of the code can be generated using the `dev.samstevens.totp.qr.ZxingPngQrGenerator` class.\n\n```java\nQrGenerator generator = new ZxingPngQrGenerator();\nbyte[] imageData = generator.generate(data)\n```\n\nThe `generate` method returns a byte array of the raw image data. The mime type of the data that is generated by the generator can be retrieved using the `getImageMimeType` method.\n\n```java\nString mimeType = generator.getImageMimeType();\n// mimeType = \"image/png\"\n```\n\nThe image data can then be outputted to the browser, or saved to a temporary file to show it to the user.\n\n#### Embedding the QR code within HTML\n\nTo avoid the QR code image having to be saved to disk, or passing the shared secret to another endpoint that generates and returns the image, it can be encoded in a [Data URI](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs), and embedded directly in the HTML served to the user.\n\n```java\nimport static dev.samstevens.totp.util.Utils.getDataUriForImage;\n...\nString dataUri = getDataUriForImage(imageData, mimeType);\n// dataUri = data:image/png;base64,iVBORw0KGgoAAAANSU...\n```\n\nThe QR code image can now be embedded directly in HTML via the data URI. Below is an example using [Thymeleaf](https://www.thymeleaf.org/):\n\n```html\n\u003cimg th:src=\"${dataUri}\" /\u003e\n```\n\n\n\n### Verifying one time passwords\n\nAfter a user sets up their MFA, it's a good idea to get them to enter two of the codes generated by their app to verify the setup was successful. To verify a code submitted by the user, do the following:\n\n```java\nTimeProvider timeProvider = new SystemTimeProvider();\nCodeGenerator codeGenerator = new DefaultCodeGenerator();\nCodeVerifier verifier = new DefaultCodeVerifier(codeGenerator, timeProvider);\n\n// secret = the shared secret for the user\n// code = the code submitted by the user\nboolean successful = verifier.isValidCode(secret, code)\n```\n\nThis same process is used when verifying the submitted code every time the user needs to in the future.\n\n#### Using different hashing algorithms\n\nBy default, the `DefaultCodeGenerator` uses the SHA1 algorithm to generate/verify codes, but SHA256 and SHA512 are also supported. To use a different algorithm, pass in the desired `HashingAlgorithm` into the constructor:\n\n```java\nCodeGenerator codeGenerator = new DefaultCodeGenerator(HashingAlgorithm.SHA512);\n```\n\nWhen verifying a given code, **you must use the same hashing algorithm** that was specified when the QR code was generated for the secret, otherwise the user submitted codes will not match.\n\n#### Setting the time period and discrepancy\n\nThe one time password codes generated in the authenticator apps only last for a certain time period before they are re-generated, and most implementations of TOTP allow room for codes that have recently expired, or will only \"become valid\" soon in the future to be accepted as valid, to allow for a small time drift between the server and the authenticator app (discrepancy).\n\nBy default on a `DefaultCodeVerifier` the time period is set to the standard 30 seconds, and the discrepancy to 1, to allow a time drift of +/-30 seconds. These values can be changed by calling the appropriate setters:\n\n```java\nDefaultCodeVerifier verifier = new DefaultCodeVerifier(codeGenerator, timeProvider);\n// sets the time period for codes to be valid for to 60 seconds\nverifier.setTimePeriod(60);\n\n// allow codes valid for 2 time periods before/after to pass as valid\nverifier.setAllowedTimePeriodDiscrepancy(2);\n```\n\nLike the hashing algorithm, **the time period must be the same** as the one specified when the QR code for the secret was created.\n\n#### Setting how many digits long the generated codes are\n\nMost TOTP implementations generate codes that are 6 digits long, but codes can have a length of any positive non-zero integer. The default number of digits in a code generated by a `DefaultCodeGenerator` instance is 6, but can be set to a different value by passing the number as the second parameter in the constructor:\n\n```java\nCodeGenerator codeGenerator = new DefaultCodeGenerator(HashingAlgorithm.SHA1, 4);\n```\n\nThe above generator will generate codes of 4 digits, using the SHA1 algorithm.\n\nOnce again, **the number of digits must be the same** as what was specified when the QR code for the secret was created.\n\n\n\n### Using different time providers\n\nWhen verifying user submitted codes with a `DefaultCodeVerifier`, a `TimeProvider` is needed to get the current time (unix) time. In the example code above, a `SystemTimeProvider` is used, but this is not the only option.\n\n#### Getting the time from the system\n\nMost applications should be able to use the `SystemTimeProvider` class to provide the time, which gets the time from the system clock.  If the system clock is reliable, it is reccomended that this provider is used.\n\n\n#### Getting the time from an NTP Server\n\nIf the system clock cannot be used to accurately get the current time, then you can fetch it from an NTP server with the `dev.samstevens.totp.time.NtpTimeProvider` class, passing in the NTP server hostname you wish you use.\n\n```java\nTimeProvider timeProvider = new NtpTimeProvider(\"pool.ntp.org\");\n```\n\nThe default timeout for the requests to the NTP server is 3 seconds, but this can be set by passing in the desired number of milliseconds as the second parameter in the constructor:\n\n```java\nTimeProvider timeProvider = new NtpTimeProvider(\"pool.ntp.org\", 5000);\n```\n\n**Using this time provider requires that the [Apache Commons Net](https://commons.apache.org/proper/commons-net) library is available on the classpath**. Add the dependency to your project with Maven/Gradle like this:\n\n**Maven**:\n\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003ecommons-net\u003c/groupId\u003e\n  \u003cartifactId\u003ecommons-net\u003c/artifactId\u003e\n  \u003cversion\u003e3.6\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n**Gradle**:\n\n```\ndependencies {\n  compile 'commons-net:commons-net:3.6'\n}\n```\n\n### Recovery Codes\n\nRecovery codes can be used to allow users to gain access to their MFA protected account without providing a TOTP, bypassing the MFA process. This is usually given as an option to the user so that in the event of losing access to the device which they have registered the MFA secret with, they are still able to log in.\n\nUsually, upon registering an account for MFA, several one-time use codes will be generated and presented to the user, with instructions to keep them very safe. When the user is presented with the prompt for a TOTP in the future, they can opt to enter one of the recovery codes instead to gain access to their account. \n\nMost of the logic needed for implementing recovery codes (storage, associating them with a user, checking for existance, etc) is implementation specific, but the codes themselves can be generated via this library.\n\nThe default implementation provided in this library generates recovery codes :\n\n- of 16 characters\n- composed of numbers and lower case characters from latin alphabet (for a total of 36 possible characters)\n- split in groups separated with dash for better readability\n\nThoses settings guarantees recovery codes security (with an entropy of 82 bits) while keeping codes simple to read and enter by end user when needed.\n\n\n```java\nimport dev.samstevens.totp.recovery.RecoveryCodeGenerator;\n...\n// Generate 16 random recovery codes\nRecoveryCodeGenerator recoveryCodes = new RecoveryCodeGenerator();\nString[] codes = recoveryCodes.generateCodes(16);\n// codes = [\"tf8i-exmo-3lcb-slkm\", \"boyv-yq75-z99k-r308\", \"w045-mq6w-mg1i-q12o\", ...]\n```\n\n\n\n## Running Tests\n\nTo run the tests for the library with Maven, run `mvn test`.\n\n\n\n\n## License\n\nThis project is licensed under the [MIT license](https://opensource.org/licenses/MIT).\n","funding_links":[],"categories":["安全"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsamdjstevens%2Fjava-totp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsamdjstevens%2Fjava-totp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsamdjstevens%2Fjava-totp/lists"}