{"id":25063078,"url":"https://github.com/robinohs/totp-kt","last_synced_at":"2025-04-14T16:34:14.178Z","repository":{"id":39965737,"uuid":"506796631","full_name":"robinohs/totp-kt","owner":"robinohs","description":"A kotlin implementation of HOTP (RFC-4226) and TOTP (RFC-6238). Supports validation and generation of 2-factor authentication codes, recovery codes and randomly secure secrets.","archived":false,"fork":false,"pushed_at":"2023-02-20T13:49:57.000Z","size":922,"stargazers_count":26,"open_issues_count":2,"forks_count":5,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-28T05:12:22.896Z","etag":null,"topics":["2fa","2factor","hotp","java","kotlin","mfa","one-time-password","otp","otp-verification","rfc-4226","rfc-6238","totp","totp-generator"],"latest_commit_sha":null,"homepage":"https://robinohs.github.io/totp-kt/","language":"Kotlin","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/robinohs.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}},"created_at":"2022-06-23T21:28:52.000Z","updated_at":"2025-03-17T13:20:02.000Z","dependencies_parsed_at":"2025-02-06T17:41:12.380Z","dependency_job_id":"ae403903-7ceb-4472-bb6c-9f15fd58b3b2","html_url":"https://github.com/robinohs/totp-kt","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robinohs%2Ftotp-kt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robinohs%2Ftotp-kt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robinohs%2Ftotp-kt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robinohs%2Ftotp-kt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/robinohs","download_url":"https://codeload.github.com/robinohs/totp-kt/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248916883,"owners_count":21182882,"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":["2fa","2factor","hotp","java","kotlin","mfa","one-time-password","otp","otp-verification","rfc-4226","rfc-6238","totp","totp-generator"],"created_at":"2025-02-06T17:41:03.360Z","updated_at":"2025-04-14T16:34:14.147Z","avatar_url":"https://github.com/robinohs.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# totp-kt - Kotlin OTP Library\n\n**Visit the documentation at: https://robinohs.github.io/totp-kt/**\n\n[![SonarCloud](https://sonarcloud.io/images/project_badges/sonarcloud-black.svg)](https://sonarcloud.io/summary/new_code?id=robinohs_totp-kt)\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![CircleCI](https://dl.circleci.com/status-badge/img/gh/robinohs/totp-kt/tree/main.svg?style=svg)](https://dl.circleci.com/status-badge/redirect/gh/robinohs/totp-kt/tree/main) [![codecov](https://codecov.io/gh/robinohs/totp-kt/branch/main/graph/badge.svg?token=2OT80TLHK9)](https://codecov.io/gh/robinohs/totp-kt) [![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=robinohs_totp-kt\u0026metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=robinohs_totp-kt) [![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=robinohs_totp-kt\u0026metric=security_rating)](https://sonarcloud.io/summary/new_code?id=robinohs_totp-kt) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=robinohs_totp-kt\u0026metric=alert_status)](https://sonarcloud.io/summary/new_code?id=robinohs_totp-kt) [![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=robinohs_totp-kt\u0026metric=code_smells)](https://sonarcloud.io/summary/new_code?id=robinohs_totp-kt) [![deploy-docu](https://github.com/robinohs/totp-kt/actions/workflows/deploy-docu.yml/badge.svg?branch=docusaurus)](https://github.com/robinohs/totp-kt/actions/workflows/deploy-docu.yml)\n\nNative Kotlin library for time-based TOTP and HMAC-based HOTP one-time passwords.\nEnables the developer to:\n- validate and generate TOTP [(RFC 6238)](https://datatracker.ietf.org/doc/html/rfc6238) and HOTP [(RFC 4226)](https://datatracker.ietf.org/doc/html/rfc4226) one-time passwords,\n- generate randomly secure secrets to use with authenticators,\n- generate randomly secure recovery codes.\n\n# Navigation\n - [Installation](#installation)\n   - [Maven](#maven) \n     - [Kotlin DSL](#kotlin-dsl)\n     - [Maven](#maven)\n     - [Gradle](#gradle) \n   - [Jitpack](#jitpack)\n      - [Kotlin DSL](#kotlin-dsl)\n      - [Maven](#maven)\n      - [Gradle](#gradle)\n - [Usage](#usage)\n    - [TOTP (Time-based One-Time Password)](#totp-time-based-one-time-password)\n    - [HOTP (HMAC-based One-Time Password)](#hotp-hmac-based-one-time-password)\n    - [Secret generator](#secret-generator)\n    - [Recovery-Code generator](#recovery-code-generator)\n    - [Random Generator](#random-generator)\n - [Spring Boot](#spring-boot)\n - [License](#license)\n\n\n# Installation\n\n## Maven\n\n#### Kotlin DSL\nAdd the dependency:\n```kotlin\n//build.gradle.kts\ndependencies {\n  implementation(\"dev.robinohs:totp-kt:v1.0.1\")\n}\n```\n#### Maven\nAdd the dependency:\n```xml\n\u003c!--pom.xml--\u003e\n\u003cdependency\u003e\n    \u003cgroupId\u003edev.robinohs\u003c/groupId\u003e\n    \u003cartifactId\u003etotp-kt\u003c/artifactId\u003e\n    \u003cversion\u003e1.0.1\u003c/version\u003e\n\u003c/dependency\u003e\n```\n#### Gradle\nAdd the dependency:\n```groovy\n//build.gradle\ndependencies {\n  implementation 'dev.robinohs:totp-kt:v1.0.1'\n}\n```\n\n## Jitpack\nIf you are using Jitpack as a repository, you can follow one of the following sections to install using with your favorite package manager such as gradle or maven.\n\n#### Kotlin DSL\nAdd Jitpack to repositories:\n```kotlin\n//build.gradle.kts\nrepositories {  \n  mavenCentral()  \n  maven { url = uri(\"https://jitpack.io\") }  \n}\n```\nAdd the dependency:\n```kotlin\n//build.gradle.kts\ndependencies {\n  implementation(\"com.github.robinohs:totp-kt:v1.0.1\")\n}\n```\n#### Maven\nAdd Jitpack to repositories:\n```xml\n\u003c!--pom.xml--\u003e\n\u003crepositories\u003e\n  \u003crepository\u003e\n    \u003cid\u003ejitpack.io\u003c/id\u003e\n    \u003curl\u003ehttps://jitpack.io\u003c/url\u003e\n  \u003c/repository\u003e\n\u003c/repositories\u003e\n```\nAdd the dependency:\n```xml\n\u003c!--pom.xml--\u003e\n\u003cdependency\u003e\n  \u003cgroupId\u003ecom.github.robinohs\u003c/groupId\u003e\n    \u003cartifactId\u003etotp-kt\u003c/artifactId\u003e\n    \u003cversion\u003ev1.0.1\u003c/version\u003e\n\u003c/dependency\u003e\n```\n#### Gradle\nAdd Jitpack to repositories:\n```groovy\n//build.gradle\nallprojects {\n  repositories {\n    //...\n    maven { url 'https://jitpack.io' }\n  }\n}\n```\nAdd the dependency:\n```groovy\n//build.gradle\ndependencies {\n  implementation 'com.github.robinohs:totp-kt:v1.0.1'\n}\n```\n# Usage\n## TOTP (Time-based One-Time Password)\nThe time-based one-time password method, generates one-time passwords by using a shared secret in combination with a time window as the source of uniqueness. The TOTP algorithm is an extension of [HOTP](#hotp-hmac-based-one-time-password). The algorithm is used by commonly known authenticator apps, e.g. Google Authenticator, Mircrosoft Authenticator and others.\n\n### TOTP flow\n```mermaid\nsequenceDiagram\nparticipant Client\nparticipant Server\nClient -\u003e Server: shared secret\nClient -\u003e\u003e Server: login (name: xy, password: xy)\nServer -\u003e\u003e Client : 401 TOTP required\nClient -\u003e\u003e Client: Client generates TOTP\nClient -\u003e\u003e Server: login (name: xy, password: xy, totp: 564867)\nServer -\u003e\u003e Server: Server generates TOTP\nServer -\u003e\u003e Server: Is client TOTP the same?\nServer --\u003e\u003e Client: If equal: JWT (session, ...)\nServer --\u003e\u003e Client: If different: 401, BadCredentials\n```\n### Create a TOTP generator\nYou can create an instance of the TotpGenerator in the following way:\n```kotlin\nval totpGenerator = TotpGenerator()\n```\n### Use the TOTP generator\n#### Method: Generate Code\nAfter you created the totpGenerator instance you can generate a one-time password by calling the generatore code method with the secret as an argument. Optionally, if you want to specify a specific time and not have the generator to take the current time itself, you can pass a time as an argument.\n```kotlin\nval secret = some_base32_encoded_secret_as_bytearray\nval code = totpGenerator.generateCode(secret)\n```\nIf one would like to specify a time:\n```kotlin\n// with millis\ntotpGenerator.generateCode(secret, 1656459878681)\n// with Instant\ntotpGenerator.generateCode(secret, Instant())\n// with Date\ntotpGenerator.generateCode(secret, Date())\n```\n#### Method: Validate Code\nThere is a helper function to compare a currently generated code with a given code. Optionally, you can also use generateCode yourself and compare the resulting string to the client's code.\n```kotlin\nval secret = some_base32_encoded_secret_as_bytearray\nval clientCode = given_client_code\ntotpGenerator.isCodeValid(secret, clientCode)\n```\nIf one would like to specify a time:\n```kotlin\n// with millis\ntotpGenerator.isCodeValid(secret, 1656459878681, clientCode)\n// with Instant\ntotpGenerator.isCodeValid(secret, Instant(), clientCode)\n// with Date\ntotpGenerator.isCodeValid(secret, Date(), clientCode)\n```\n#### Method: Validate Code With Tolerance\nCompares a generated code to a given code using a counter derived from the current timestamp and a given secret. \nIn addition, the method considers a tolerance and also checks the given code against a number of previous tokens\nequal to the tolerance. Returns true if the given code matches any of these tokens.\n```kotlin\nval secret = some_base32_encoded_secret_as_bytearray\nval clientCode = given_client_code\ntotpGenerator.isCodeValidWithTolerance(secret, clientCode)\n```\nIf one would like to specify a time:\n```kotlin\n// with millis\ntotpGenerator.isCodeValidWithTolerance(secret, 1656459878681, clientCode)\n// with Instant\ntotpGenerator.isCodeValidWithTolerance(secret, Instant(), clientCode)\n// with Date\ntotpGenerator.isCodeValidWithTolerance(secret, Date(), clientCode)\n```\n#### Method: Timeslot beginning\nCalculates the start timestamp of the time slot in which the actual or given timestamp lies.\n```kotlin\n// takes actual timestamp from clock, returns millis\ntotpGenerator.calculateTimeslotBeginning()\n```\nIf one would like to specify a time:\n```kotlin\n// with millis, returns millis\ntotpGenerator.calculateTimeslotBeginning(1656459878681, clientCode)\n// with Instant, returns Instant\ntotpGenerator.calculateTimeslotBeginning(Instant(), clientCode)\n// with Date, returns Date\ntotpGenerator.calculateTimeslotBeginning(Date(), clientCode)\n```\n#### Method: Remaining time\nCalculates the remaining duration of the time slot in which the actual or given timestamp lies.\n```kotlin\n// takes actual timestamp from clock\ntotpGenerator.calculateRemainingTime()\n```\nIf one would like to specify a time:\n```kotlin\n// with millis\ntotpGenerator.calculateRemainingTime(1656459878681)\n// with Instant\ntotpGenerator.calculateRemainingTime(Instant())\n// with Date\ntotpGenerator.calculateRemainingTime(Date())\n```\n### Customize properties\nIt is possible to customize the properties of the generator, either by setters or applying them in the constructor.\n#### Clock\nThe clock is the time source for the generator if no time is passed as an argument to the generateCode or validateCode method.\n```kotlin\nval totpGenerator = TotpGenerator(clock = Clock.systemUTC())\n// or\ntotpGenerator.clock = Clock.systemUTC()\n```\n\u003e For testing purposes, one could assign a **Clock.fixed** that always returns the same timestamp and thus the same TOTP code.\n#### Time period\nThe time period is the duration of every time step in which the generated code is the same. This is needed as due to delays (e.g., network delay) the server will not generate the code with the same timestamp as the client. As a compromise between security and usability the default time step is set as 30 seconds.\n\u003e A time period of 30 seconds is used by the Google or Mircrosoft Authenticator app.\n#### Tolerance\nThe tolerance specifies a number of previous tokens that are also accepted as valid tokens in addition to the current valid token.\n\u003e A tolerance of 1 token is set as default. In [RFC6238#5.2](https://datatracker.ietf.org/doc/html/rfc6238#section-5.2) \n\u003e a time step is recommended to compensate for delays such as network delay.\n#### Code length\nThe code length specifies how long a generated code will be. If the code length is changed, it is necessary that the user's authenticator app supports this as well.\n\u003e A length of 6 digits is used by the Google or Microsoft Authenticator app.\n## HOTP (HMAC-based One-Time Password)\nThe HMAC-based one-time password method generates one-time passwords by using a shared secret in combination with a counter as the source of uniqueness. The major problem of this approach is the synchronization of the counter between the client and the server. Synchronization is out of scope for this library and therefore needs to be implemented by the consumer. A method for re-synchronization is described in the specification [RFC4226#7.4](https://datatracker.ietf.org/doc/html/rfc4226#section-7.4).\n\n\n### HOTP flow\n```mermaid\nsequenceDiagram\nparticipant Client\nparticipant Server\nClient -\u003e Server: shared secret + counter\nClient -\u003e\u003e Server: login (name: xy, password: xy)\nServer -\u003e\u003e Client : 401 HOTP required\nClient -\u003e\u003e Client: Client generates HOTP with counter\nClient -\u003e\u003e Server: login (name: xy, password: xy, hotp: 564867)\nServer -\u003e\u003e Server: Server generates HOTP with counter\nServer -\u003e\u003e Server: Is client HOTP the same?\nServer --\u003e\u003e Client: If equal: JWT (session, ...)\nServer --\u003e\u003e Client: If different: 401, BadCredentials\n```\n### Create a HOTP generator\nYou can create an instance of the HotpGenerator in the following way:\n```kotlin\nval hotpGenerator = HotpGenerator()\n```\n### Use the HOTP generator\n#### Method: Generate Code\nAfter you created the hotpGenerator instance you can generate a one-time password by calling the generatore code method with the secret and the counter as arguments.\n```kotlin\nval secret = some_base32_encoded_secret_as_bytearray\nval counter = some_number\nval code = hotpGenerator.generateCode(secret, counter)\n```\n#### Method: Validate Code\nThere is a helper function to compare a generated code with a given code. Optionally, you could also use generateCode yourself and compare the resulting string to the client's code.\n```kotlin\nval secret = some_base32_encoded_secret_as_bytearray\nval counter = some_number\nval clientCode = given_client_code\ntotpGenerator.isCodeValid(secret, counter, clientCode)\n```\n### Customize properties\nIt is possible to customize the properties of the generator, either by setters or applying them in the constructor.\n#### Code length\nThe code length specifies how long a generated code will be. If the code length is changed, it is necessary that the user's authenticator app supports this as well.\n\n## Secret generator\nThe secret generator can be used to generate base32 encoded secrets as strings and bytearrays.\n### Create a Secret  generator\nYou can create an instance of the SecretGenerator in the following way:\n```kotlin\nval secretGenerator = SecretGenerator()\n```\n### Use the Secret generator\n#### Method: Generate Secret\nIf you want to generate a secret that can be used as a shared secret between the client and the server, there is the generateSecret function. The default behavior of the function is to generate a 10 character secret and convert it to a Base32Secret instance. Optionally you can specify the length of the plain input to the base32 encoding secret.\n```kotlin\nval base32Secret: Base32Secret = secretGenerator.generateSecret()\n```\n#### Data Class: Base32Secret\nThe Base32Secret data class contains a secret in the form of a bytearray and a string.\n```kotlin\nval base32Secret: Base32Secret = secretGenerator.generateSecret()\nval (secretAsString, secretAsByteArray) = base32Secret\n```\n### Customize properties\nIt is possible to customize the properties of the generator, either by setters or applying them in the constructor.\n#### Used Random generator\nThe RandomGenerator instance used internally to generate random strings.\n\n## Recovery-Code generator\nThis generator can be used to create a randomly generated string in block form.\n### Create a Recovery-Code generator\nYou can create an instance of the RecoveryCodeGenerator in the following way:\n```kotlin\nval recoveryCodeGenerator = RecoveryCodeGenerator()\n```\n### Use the Recovery-Code generator\n#### Method: Generate Recovery-Code\nThis method generates a single recovery-code.\n```kotlin\nval recoveryCode = recoveryCodeGenerator.generateRecoveryCode()\nprintln(recoveryCode)\n\"AAAA-BBBB-CCCC-DDDD\"\n```\n#### Method: Generate a list of Recovery-Codes\nThis method generates a list of recovery-codes.\n```kotlin\nval recoveryCodes = recoveryCodeGenerator.generateRecoveryCodes(3)\nprintln(recoveryCodes)\n[\"AAAA-BBBB-CCCC-DDDD\", \"BBBB-AAAA-CCCC-DDDD\", \"BBBB-AAAA-DDDD-CCCC\"]\n```\n### Customize properties\nIt is possible to customize the properties of the generator, either by setters or applying them in the constructor.\n#### Number of blocks\nSpecifies the number of blocks that make up each recovery code.\n#### Block length\nSpecifies the length of each block in each recovery code.\n#### Used Random generator\nThe RandomGenerator instance used internally to generate random strings.\n\n## Random generator\nThe random generator is internally used by the [SecretGenerator](#secret-generator) and [RecoveryCodeGenerator](#secret-generator) to create randomly secure strings.\n### Customize properties\nIt is possible to customize the properties of the generator, either by setters or applying them in the constructor.\n#### Random (Dangerous)\nThe generator accepts any class implementing the java random interface.\n\u003e The default is the SecureRandom implementation and should not be changed unless you know what you are doing!\n#### Charpool (Dangerous)\nThe char pool specifies the list of characters that can be used to generate the string.\n\u003e If the char pool gets too small, the security is weakend. For example a 10-character long password with the default charset of the library has the following properties:$$\nCombinations: 62^{10} = 8.3929937e^{17}\\\\\nEntropy: log_2(62^{10}) = 59.542\n$$\nPasswords with a entropy \u003e50 are considered to be secure.\n## Spring Boot\nInstead of creating a new instance of a generator each time a token is checked, it is also possible to create a bean within Spring.\nThis allows to configure the generator once and this configuration is maintained each time the bean is injected into a component. \n```kotlin\n@Bean  \nfun totpGenerator(): TotpGenerator {  \n    val generator = TotpGenerator()  \n    generator.codeLength = 9\n    generator.timePeriod = Duration.ofSeconds(20)\n    return generator\n}  \n  \n@Bean  \nfun recoveryCodeGenerator(): RecoveryCodeGenerator {  \n    val generator = RecoveryCodeGenerator()\n    generator.blockLength = 5\n    return generator\n}\n```\nThis bean can then be injected in the constructor of any class marked with @Component (@Service, ...).\n```kotlin\n@Component  \nclass CustomComponent(  \n  private val totpGenerator: TotpGenerator,\n  private val recoveryCodeGenerator: RecoveryCodeGenerator\n) {\n\t//...\n}\n```\n# License\n[MIT](https://github.com/robinohs/totp-kt/blob/master/LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobinohs%2Ftotp-kt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frobinohs%2Ftotp-kt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobinohs%2Ftotp-kt/lists"}