### Complete 2FA Implementation Example Source: https://context7.com/dashbitco/nimble_totp/llms.txt A full example demonstrating how to integrate NimbleTOTP into an Elixir application for two-factor authentication. ```APIDOC ## Complete 2FA Implementation Example A full example showing how to implement TOTP-based two-factor authentication in an Elixir application, including secret generation, QR code setup, and code validation with reuse prevention. ```elixir defmodule MyApp.TwoFactorAuth do @moduledoc """ Complete 2FA implementation using NimbleTOTP. """ # Step 1: Generate and store a secret for a user def setup_2fa(user) do secret = NimbleTOTP.secret() # Generate QR code URI for authenticator app uri = NimbleTOTP.otpauth_uri( "MyApp", # Issuer (your app name) user.email, # Account identifier secret ) # Store the secret temporarily until verified # In production, encrypt before storing {:ok, %{secret: Base.encode32(secret), uri: uri}} end # Step 2: Verify initial setup and persist secret def confirm_2fa_setup(user, code, encoded_secret) do {:ok, secret} = Base.decode32(encoded_secret) if NimbleTOTP.valid?(secret, code) do # Save the confirmed secret to user record # user |> Ecto.Changeset.change(%{totp_secret: encoded_secret}) |> Repo.update() {:ok, :2fa_enabled} else {:error, :invalid_code} end end # Step 3: Validate code during authentication (with reuse prevention) def verify_2fa(user, code) do with {:ok, secret} <- Base.decode32(user.totp_secret), true <- NimbleTOTP.valid?(secret, code, since: user.last_totp_at) do # Update last_totp_at to prevent reuse now = System.os_time(:second) # user |> Ecto.Changeset.change(%{last_totp_at: now}) |> Repo.update() {:ok, :verified} else _ -> {:error, :invalid_code} end end # Grace period implementation for slow delivery methods (e.g., SMS) def verify_with_grace_period(user, code) do {:ok, secret} = Base.decode32(user.totp_secret) time = System.os_time(:second) # Allow current code or previous period's code valid_now = NimbleTOTP.valid?(secret, code, time: time, since: user.last_totp_at) valid_prev = NimbleTOTP.valid?(secret, code, time: time - 30, since: user.last_totp_at) if valid_now or valid_prev do {:ok, :verified} else {:error, :invalid_code} end end end # Usage example: # {:ok, setup} = MyApp.TwoFactorAuth.setup_2fa(user) # # Display setup.uri as QR code to user # # User scans with authenticator app and enters code # {:ok, :2fa_enabled} = MyApp.TwoFactorAuth.confirm_2fa_setup(user, "123456", setup.secret) # # Later, during login: ``` ### Response Example ```elixir # Example output for setup_2fa: # {:ok, %{secret: "JBSWY3DPEHPK3PXP", uri: "otpauth://totp/MyApp:user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=MyApp"}} # Example output for confirm_2fa_setup: # {:ok, :2fa_enabled} # {:error, :invalid_code} # Example output for verify_2fa: # {:ok, :verified} # {:error, :invalid_code} ``` ``` -------------------------------- ### Implement 2FA in Elixir Source: https://context7.com/dashbitco/nimble_totp/llms.txt A complete module example for setting up and verifying 2FA, including secret storage and reuse prevention. ```elixir defmodule MyApp.TwoFactorAuth do @moduledoc """ Complete 2FA implementation using NimbleTOTP. """ # Step 1: Generate and store a secret for a user def setup_2fa(user) do secret = NimbleTOTP.secret() # Generate QR code URI for authenticator app uri = NimbleTOTP.otpauth_uri( "MyApp", # Issuer (your app name) user.email, # Account identifier secret ) # Store the secret temporarily until verified # In production, encrypt before storing {:ok, %{secret: Base.encode32(secret), uri: uri}} end # Step 2: Verify initial setup and persist secret def confirm_2fa_setup(user, code, encoded_secret) do {:ok, secret} = Base.decode32(encoded_secret) if NimbleTOTP.valid?(secret, code) do # Save the confirmed secret to user record # user |> Ecto.Changeset.change(%{totp_secret: encoded_secret}) |> Repo.update() {:ok, :2fa_enabled} else {:error, :invalid_code} end end # Step 3: Validate code during authentication (with reuse prevention) def verify_2fa(user, code) do with {:ok, secret} <- Base.decode32(user.totp_secret), true <- NimbleTOTP.valid?(secret, code, since: user.last_totp_at) do # Update last_totp_at to prevent reuse now = System.os_time(:second) # user |> Ecto.Changeset.change(%{last_totp_at: now}) |> Repo.update() {:ok, :verified} else _ -> {:error, :invalid_code} end end # Grace period implementation for slow delivery methods (e.g., SMS) def verify_with_grace_period(user, code) do {:ok, secret} = Base.decode32(user.totp_secret) time = System.os_time(:second) # Allow current code or previous period's code valid_now = NimbleTOTP.valid?(secret, code, time: time, since: user.last_totp_at) valid_prev = NimbleTOTP.valid?(secret, code, time: time - 30, since: user.last_totp_at) if valid_now or valid_prev do {:ok, :verified} else {:error, :invalid_code} end end end ``` -------------------------------- ### Generate OTPAuth URI for QR Code Source: https://github.com/dashbitco/nimble_totp/blob/master/README.md Generate a URI compatible with the OTPAuth specification, which can be encoded into a QR code for easy setup in authenticator apps. Requires the user identifier, the secret, and optionally the issuer name. ```elixir NimbleTOTP.otpauth_uri("Acme:alice", secret, issuer: "Acme") #=> "otpauth://totp/Acme:alice?secret=MFRGGZA&issuer=Acme" ``` -------------------------------- ### Verify 2FA Code Source: https://context7.com/dashbitco/nimble_totp/llms.txt Verifies a 2FA code provided by the user against the stored secret. This example assumes a successful verification. ```elixir {:ok, :verified} = MyApp.TwoFactorAuth.verify_2fa(user, "654321") ``` -------------------------------- ### Generate TOTP verification codes Source: https://context7.com/dashbitco/nimble_totp/llms.txt Examples of generating TOTP codes with custom digits, algorithms, and time periods. ```elixir # 8-digit code (must also use digits: 8 in valid?/3 and otpauth_uri) code = NimbleTOTP.verification_code(secret, digits: 8) #=> "56977712" ``` ```elixir # Using SHA-256 algorithm (must also specify in valid?/3 and otpauth_uri) code = NimbleTOTP.verification_code(secret, algorithm: :sha256) #=> "234567" ``` ```elixir # Combining multiple options code = NimbleTOTP.verification_code(secret, time: System.os_time(:second), period: 30, digits: 8, algorithm: :sha512 ) #=> "12345678" ``` ```elixir # Codes have leading zeros when needed secret_zeros = Base.decode32!("BKFCZBQPZOXNTER5HKHGPHPGCXBNBDNC") code = NimbleTOTP.verification_code(secret_zeros, time: 1586366951) #=> "005357" ``` -------------------------------- ### Generate OTPAuth URIs Source: https://context7.com/dashbitco/nimble_totp/llms.txt Create URIs for QR code generation compatible with standard authenticator apps. ```elixir secret = NimbleTOTP.secret() # Basic URI generation with issuer and account uri = NimbleTOTP.otpauth_uri("Acme Corp", "alice@example.com", secret) #=> "otpauth://totp/Acme%20Corp:alice@example.com?secret=MFRGGZDF...&issuer=Acme%20Corp" # With custom digit length (must match verification_code/valid? options) uri_8_digits = NimbleTOTP.otpauth_uri("Acme Corp", "alice@example.com", secret, digits: 8) #=> "otpauth://totp/Acme%20Corp:alice@example.com?secret=MFRGGZDF...&issuer=Acme%20Corp&digits=8" # With custom algorithm (SHA-256 or SHA-512) uri_sha256 = NimbleTOTP.otpauth_uri("Acme Corp", "alice@example.com", secret, digits: 8, algorithm: :sha256 ) #=> "otpauth://totp/Acme%20Corp:alice@example.com?secret=...&issuer=Acme%20Corp&digits=8&algorithm=sha256" # Generate QR code SVG using eqrcode library # First add {:eqrcode, "~> 0.1.10"} to your deps uri |> EQRCode.encode() |> EQRCode.svg(width: 200) #=> "\n "otpauth://totp/Acme%20Corp:alice@example.com?secret=MFRGGZDF...&issuer=Acme%20Corp" # With additional parameters uri = NimbleTOTP.otpauth_uri("Acme:bob@example.com", secret, issuer: "Acme", digits: 8, algorithm: :sha512 ) #=> "otpauth://totp/Acme:bob@example.com?secret=...&issuer=Acme&digits=8&algorithm=sha512" ``` ### Response #### Success Response (200) - **uri** (String.t()) - The generated OTPAuth URI. #### Response Example ```json { "uri": "otpauth://totp/Acme%20Corp:alice@example.com?secret=MFRGGZDF...&issuer=Acme%20Corp" } ``` ``` -------------------------------- ### Add NimbleTOTP Dependency Source: https://github.com/dashbitco/nimble_totp/blob/master/README.md Add NimbleTOTP to your project's dependencies in the mix.exs file. ```elixir def deps do [ {:nimble_totp, "~> 1.0"} ] end ``` -------------------------------- ### Generate and Manage Secrets Source: https://context7.com/dashbitco/nimble_totp/llms.txt Create cryptographically secure secrets for TOTP. Secrets should be stored using Base32 encoding. ```elixir # Generate a default 20-byte secret (recommended) secret = NimbleTOTP.secret() #=> <<178, 117, 46, 7, 172, 202, 108, 127, 186, 180, 42, 63, 24, 30, 95, 116, 80, 121, 106, 102>> # Check the secret size byte_size(secret) #=> 20 # Generate a custom-sized secret (e.g., 32 bytes for SHA-256) secret_32 = NimbleTOTP.secret(32) byte_size(secret_32) #=> 32 # Encode for storage (Base32 is standard for TOTP) encoded_secret = Base.encode32(secret) #=> "WJMRGR5MZJWH7K5UFLVYSM65FUEXYUGD" # Decode when retrieving from storage {:ok, decoded} = Base.decode32(encoded_secret) decoded == secret #=> true ``` -------------------------------- ### Generate Legacy OTPAuth URIs Source: https://context7.com/dashbitco/nimble_totp/llms.txt Legacy function for generating URIs using combined label format. Use otpauth_uri/4 for new implementations. ```elixir secret = NimbleTOTP.secret() # Deprecated format with combined label uri = NimbleTOTP.otpauth_uri("Acme Corp:alice@example.com", secret, issuer: "Acme Corp") #=> "otpauth://totp/Acme%20Corp:alice@example.com?secret=MFRGGZDF...&issuer=Acme%20Corp" # With additional parameters uri = NimbleTOTP.otpauth_uri("Acme:bob@example.com", secret, issuer: "Acme", digits: 8, algorithm: :sha512 ) #=> "otpauth://totp/Acme:bob@example.com?secret=...&issuer=Acme&digits=8&algorithm=sha512" ``` -------------------------------- ### NimbleTOTP.otpauth_uri/4 Source: https://context7.com/dashbitco/nimble_totp/llms.txt Generates an OTPAuth URI for authenticator apps, supporting custom issuer, account, digits, and algorithm. ```APIDOC ## NimbleTOTP.otpauth_uri/4 ### Description Generates an OTPAuth URI that can be encoded into a QR code for scanning by authenticator apps. This is the recommended function for creating URIs with a safer API that separates issuer and account parameters. The URI follows the standard OTPAuth format recognized by Google Authenticator, Authy, and other TOTP apps. ### Method `NimbleTOTP.otpauth_uri/4` ### Endpoint N/A (Function call) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters - **issuer** (String.t()) - The name of the service or organization. - **account** (String.t()) - The user's account name or email address. - **secret** (binary()) - The TOTP secret generated by `NimbleTOTP.secret/1`. - **options** (Keyword.t()) - Optional parameters: - **digits** (integer()) - The number of digits in the verification code (default: 6, accepted: 6-10). - **algorithm** (atom()) - The hashing algorithm (:sha1, :sha256, :sha512, default: :sha1). - **period** (integer()) - The time period in seconds for the code (default: 30). ### Request Example ```elixir secret = NimbleTOTP.secret() # Basic URI generation with issuer and account uri = NimbleTOTP.otpauth_uri("Acme Corp", "alice@example.com", secret) #=> "otpauth://totp/Acme%20Corp:alice@example.com?secret=MFRGGZDF...&issuer=Acme%20Corp" # With custom digit length (must match verification_code/valid? options) uri_8_digits = NimbleTOTP.otpauth_uri("Acme Corp", "alice@example.com", secret, digits: 8) #=> "otpauth://totp/Acme%20Corp:alice@example.com?secret=MFRGGZDF...&issuer=Acme%20Corp&digits=8" # With custom algorithm (SHA-256 or SHA-512) uri_sha256 = NimbleTOTP.otpauth_uri("Acme Corp", "alice@example.com", secret, digits: 8, algorithm: :sha256 ) #=> "otpauth://totp/Acme%20Corp:alice@example.com?secret=...&issuer=Acme%20Corp&digits=8&algorithm=sha256" # Generate QR code SVG using eqrcode library # First add {:eqrcode, "~> 0.1.10"} to your deps uri |> EQRCode.encode() |> EQRCode.svg(width: 200) #=> "\n "569777" # Basic validation NimbleTOTP.valid?(secret, code, time: time) #=> true NimbleTOTP.valid?(secret, "000000", time: time) #=> false # Invalid format returns false (not a binary) NimbleTOTP.valid?(secret, 123456, time: time) #=> false # Wrong length returns false NimbleTOTP.valid?(secret, "12345", time: time) #=> false # Validate with DateTime datetime = DateTime.from_unix!(time) NimbleTOTP.valid?(secret, code, time: datetime) #=> true # Validate with NaiveDateTime naive = DateTime.to_naive(datetime) NimbleTOTP.valid?(secret, code, time: naive) #=> true # Custom options must match verification_code options code_8 = NimbleTOTP.verification_code(secret, time: time, digits: 8, algorithm: :sha256) NimbleTOTP.valid?(secret, code_8, time: time, digits: 8, algorithm: :sha256) #=> true # Prevent code reuse with :since option # First use (since: nil means no previous use) NimbleTOTP.valid?(secret, code, time: time, since: nil) #=> true # Second use in same period (rejected) NimbleTOTP.valid?(secret, code, time: time, since: time) #=> false # Next period code is valid even with since set next_period = (Integer.floor_div(time, 30) + 1) * 30 next_code = NimbleTOTP.verification_code(secret, time: next_period) NimbleTOTP.valid?(secret, next_code, time: next_period, since: time) #=> true ``` -------------------------------- ### Generate Verification Codes Source: https://context7.com/dashbitco/nimble_totp/llms.txt Generate TOTP codes based on system time or specific timestamps. ```elixir secret = NimbleTOTP.secret() # Generate code using current time (default) code = NimbleTOTP.verification_code(secret) #=> "569777" # Generate code for a specific Unix timestamp code = NimbleTOTP.verification_code(secret, time: 1586367000) #=> "123456" # Generate code using DateTime datetime = ~U[2024-01-15 10:30:00Z] code = NimbleTOTP.verification_code(secret, time: datetime) #=> "789012" # Generate code using NaiveDateTime naive = ~N[2024-01-15 10:30:00] code = NimbleTOTP.verification_code(secret, time: naive) #=> "789012" # Custom period (60 seconds instead of default 30) code = NimbleTOTP.verification_code(secret, period: 60) #=> "345678" ``` -------------------------------- ### NimbleTOTP.secret/0 and NimbleTOTP.secret/1 Source: https://context7.com/dashbitco/nimble_totp/llms.txt Generates cryptographically secure random binary secrets for TOTP. Supports default size (20 bytes) or custom sizes. ```APIDOC ## NimbleTOTP.secret/1 ### Description Generates a cryptographically secure random binary to be used as the TOTP secret for a user. The default size is 20 bytes as recommended by the HOTP RFC specification. This secret should be securely stored and associated with the user's account. ### Method `NimbleTOTP.secret/0` or `NimbleTOTP.secret/1` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```elixir # Generate a default 20-byte secret (recommended) secret = NimbleTOTP.secret() #=> <<178, 117, 46, 7, 172, 202, 108, 127, 186, 180, 42, 63, 24, 30, 95, 116, 80, 121, 106, 102>> # Check the secret size byte_size(secret) #=> 20 # Generate a custom-sized secret (e.g., 32 bytes for SHA-256) secret_32 = NimbleTOTP.secret(32) byte_size(secret_32) #=> 32 # Encode for storage (Base32 is standard for TOTP) encoded_secret = Base.encode32(secret) #=> "WJMRGR5MZJWH7K5UFLVYSM65FUEXYUGD" # Decode when retrieving from storage {:ok, decoded} = Base.decode32(encoded_secret) decoded == secret #=> true ``` ### Response #### Success Response (200) - **secret** (binary) - A cryptographically secure random binary secret. #### Response Example ```json { "secret": <<178, 117, 46, 7, 172, 202, 108, 127, 186, 180, 42, 63, 24, 30, 95, 116, 80, 121, 106, 102>> } ``` ``` -------------------------------- ### NimbleTOTP.verification_code/2 Source: https://context7.com/dashbitco/nimble_totp/llms.txt Generates a Time-based One-Time Password (TOTP) code. Supports customization of digits, algorithm, time, and period. ```APIDOC ## NimbleTOTP.verification_code/2 Generates a TOTP code based on a secret and optional parameters. ### Parameters #### Options - **time** (integer) - Optional - The Unix timestamp (in seconds) to use for code generation. Defaults to the current time. - **period** (integer) - Optional - The time step in seconds. Defaults to 30 seconds. - **digits** (integer) - Optional - The number of digits in the generated code. Defaults to 6. - **algorithm** (:sha1, :sha256, :sha512) - Optional - The HMAC algorithm to use. Defaults to :sha1. ### Request Example ```elixir secret = NimbleTOTP.secret() # Default 6-digit code code = NimbleTOTP.verification_code(secret) # 8-digit code code_8_digits = NimbleTOTP.verification_code(secret, digits: 8) # Using SHA-256 algorithm code_sha256 = NimbleTOTP.verification_code(secret, algorithm: :sha256) # Combining multiple options code_custom = NimbleTOTP.verification_code(secret, time: System.os_time(:second), period: 30, digits: 8, algorithm: :sha512 ) # Codes have leading zeros when needed secret_zeros = Base.decode32!("BKFCZBQPZOXNTER5HKHGPHPGCXBNBDNC") code_leading_zeros = NimbleTOTP.verification_code(secret_zeros, time: 1586366951) #=> "005357" ``` ### Response Example ```elixir #=> "569777" #=> "56977712" #=> "234567" #=> "12345678" #=> "005357" ``` ``` -------------------------------- ### NimbleTOTP.verification_code/2 Source: https://context7.com/dashbitco/nimble_totp/llms.txt Generates a Time-Based One-Time Password (TOTP) for a given secret, supporting custom time, period, digits, and algorithm. ```APIDOC ## NimbleTOTP.verification_code/2 ### Description Generates a Time-Based One-Time Password for the given secret. By default, it generates a 6-digit code using the current system time with a 30-second period. The code changes every period and can be customized with different time sources, periods, digit lengths, and hashing algorithms. ### Method `NimbleTOTP.verification_code/2` ### Endpoint N/A (Function call) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters - **secret** (binary()) - The TOTP secret generated by `NimbleTOTP.secret/1`. - **options** (Keyword.t()) - Optional parameters: - **time** (DateTime.t() | NaiveDateTime.t() | posix_seconds()) - The time to generate the code for (default: current system time). - **period** (integer()) - The time period in seconds for the code (default: 30). - **digits** (integer()) - The number of digits in the verification code (default: 6, accepted: 6-10). - **algorithm** (atom()) - The hashing algorithm (:sha1, :sha256, :sha512, default: :sha1). ### Request Example ```elixir secret = NimbleTOTP.secret() # Generate code using current time (default) code = NimbleTOTP.verification_code(secret) #=> "569777" # Generate code for a specific Unix timestamp code = NimbleTOTP.verification_code(secret, time: 1586367000) #=> "123456" # Generate code using DateTime datetime = ~U[2024-01-15 10:30:00Z] code = NimbleTOTP.verification_code(secret, time: datetime) #=> "789012" # Generate code using NaiveDateTime naive = ~N[2024-01-15 10:30:00] code = NimbleTOTP.verification_code(secret, time: naive) #=> "789012" # Custom period (60 seconds instead of default 30) code = NimbleTOTP.verification_code(secret, period: 60) #=> "345678" ``` ### Response #### Success Response (200) - **code** (String.t()) - The generated Time-Based One-Time Password. #### Response Example ```json { "code": "569777" } ``` ``` -------------------------------- ### Generate and Validate TOTP Source: https://github.com/dashbitco/nimble_totp/blob/master/README.md Generate a Time-Based One-Time Password for a given secret and validate a provided code against it. The validation checks if the provided code is valid for the current or adjacent time steps. ```elixir NimbleTOTP.verification_code(secret) #=> "569777" ``` ```elixir NimbleTOTP.valid?(secret, "569777") #=> true ``` -------------------------------- ### Generate a Random Secret Source: https://github.com/dashbitco/nimble_totp/blob/master/README.md Generate a secret composed of random bytes. This secret is used for TOTP generation and validation. ```elixir secret = NimbleTOTP.secret() #=> <<63, 24, 42, 30, 95, 116, 80, 121, 106, 102>> ``` -------------------------------- ### NimbleTOTP.valid?/3 Source: https://context7.com/dashbitco/nimble_totp/llms.txt Validates a user-provided TOTP code against a secret. Supports time-based validation and prevention of code reuse. ```APIDOC ## NimbleTOTP.valid?/3 Validates a user-provided TOTP code against the secret. Returns `true` if the code matches the expected value for the given time period, `false` otherwise. Includes support for preventing code reuse via the `:since` option and implements constant-time comparison to prevent timing attacks. ### Parameters #### Path Parameters - **secret** (binary) - The secret key used for TOTP generation. - **code** (binary) - The user-provided TOTP code to validate. #### Query Parameters - **time** (integer or DateTime or NaiveDateTime) - Optional - The timestamp or DateTime object to use for validation. Defaults to the current time. - **period** (integer) - Optional - The time step in seconds. Must match the period used for code generation. Defaults to 30 seconds. - **digits** (integer) - Optional - The number of digits in the code. Must match the digits used for code generation. Defaults to 6. - **algorithm** (:sha1, :sha256, :sha512) - Optional - The HMAC algorithm used. Must match the algorithm used for code generation. Defaults to :sha1. - **since** (integer or DateTime or NaiveDateTime) - Optional - A timestamp or DateTime object representing the last time a valid code was used. If provided, codes generated at or before this time will be rejected, preventing reuse within the same period. ### Request Example ```elixir secret = NimbleTOTP.secret() time = System.os_time(:second) # Generate a valid code code = NimbleTOTP.verification_code(secret, time: time) # Basic validation NimbleTOTP.valid?(secret, code, time: time) #=> true NimbleTOTP.valid?(secret, "000000", time: time) #=> false # Invalid format returns false (not a binary) NimbleTOTP.valid?(secret, 123456, time: time) #=> false # Wrong length returns false NimbleTOTP.valid?(secret, "12345", time: time) #=> false # Validate with DateTime datetime = DateTime.from_unix!(time) NimbleTOTP.valid?(secret, code, time: datetime) #=> true # Validate with NaiveDateTime naive = DateTime.to_naive(datetime) NimbleTOTP.valid?(secret, code, time: naive) #=> true # Custom options must match verification_code options code_8 = NimbleTOTP.verification_code(secret, time: time, digits: 8, algorithm: :sha256) NimbleTOTP.valid?(secret, code_8, time: time, digits: 8, algorithm: :sha256) #=> true # Prevent code reuse with :since option # First use (since: nil means no previous use) NimbleTOTP.valid?(secret, code, time: time, since: nil) #=> true # Second use in same period (rejected) NimbleTOTP.valid?(secret, code, time: time, since: time) #=> false # Next period code is valid even with since set next_period = (Integer.floor_div(time, 30) + 1) * 30 next_code = NimbleTOTP.verification_code(secret, time: next_period) NimbleTOTP.valid?(secret, next_code, time: next_period, since: time) #=> true ``` ### Response Example ```elixir #=> true #=> false #=> false #=> false #=> true #=> true #=> true #=> false #=> true ``` ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.