rotp 6.3.0 → 6.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.devcontainer/devcontainer.json +11 -30
- data/.github/dependabot.yml +6 -0
- data/.github/workflows/release.yaml +63 -21
- data/.github/workflows/test.yaml +3 -4
- data/.release-please-manifest.json +1 -1
- data/CHANGELOG.md +11 -0
- data/README.md +5 -2
- data/lib/rotp/arguments.rb +11 -2
- data/lib/rotp/cli.rb +2 -2
- data/lib/rotp/otp/uri.rb +7 -3
- data/lib/rotp/otp.rb +8 -2
- data/lib/rotp/version.rb +1 -1
- data/release-please-config.json +4 -1
- data/spec/lib/rotp/hotp_spec.rb +2 -1
- data/spec/lib/rotp/otp/uri_spec.rb +39 -0
- data/spec/lib/rotp/totp_spec.rb +1 -1
- data/spec/spec_helper.rb +7 -0
- metadata +7 -7
- data/.devcontainer/Dockerfile +0 -19
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 19178f891005be16f5ade5f33a43c8cc77a38e1ccdd81ca62285b32cc2dd3090
|
|
4
|
+
data.tar.gz: 40f54eaf3c58f09af3c9fe55aa232c97a49a4df581f3293f04bd906f89f1df06
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f2ad58aa47f5781b2a15891a1cad38a3cab52e9b93a6e4b7ade3e0d18bce1e887d25369f3fdfd11e0752992cd78f66fccf2cc2cefcdb83671af4d73776eb3d3d
|
|
7
|
+
data.tar.gz: e3f243779f0dab586cb8307ebc40fed5ed4572d314b056a766a00baaa949971e085fb05557c23947715d5205e0048b0162554d69392e4772304e611fdceee8ce
|
|
@@ -1,41 +1,22 @@
|
|
|
1
|
-
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
|
2
|
-
// https://github.com/
|
|
1
|
+
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
|
2
|
+
// README at: https://github.com/devcontainers/templates/tree/main/src/ruby
|
|
3
3
|
{
|
|
4
4
|
"name": "Ruby",
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
// Use -bullseye variants on local on arm64/Apple Silicon.
|
|
11
|
-
"VARIANT": "3-bullseye",
|
|
12
|
-
// Options
|
|
13
|
-
"NODE_VERSION": "16"
|
|
14
|
-
}
|
|
15
|
-
},
|
|
5
|
+
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
|
|
6
|
+
"image": "mcr.microsoft.com/devcontainers/ruby:1-3.3-bullseye"
|
|
7
|
+
|
|
8
|
+
// Features to add to the dev container. More info: https://containers.dev/features.
|
|
9
|
+
// "features": {},
|
|
16
10
|
|
|
17
|
-
// Configure tool-specific properties.
|
|
18
|
-
"customizations": {
|
|
19
|
-
// Configure properties specific to VS Code.
|
|
20
|
-
"vscode": {
|
|
21
|
-
// Add the IDs of extensions you want installed when the container is created.
|
|
22
|
-
"extensions": [
|
|
23
|
-
"rebornix.Ruby"
|
|
24
|
-
]
|
|
25
|
-
}
|
|
26
|
-
},
|
|
27
|
-
|
|
28
11
|
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
|
29
12
|
// "forwardPorts": [],
|
|
30
13
|
|
|
31
14
|
// Use 'postCreateCommand' to run commands after the container is created.
|
|
32
15
|
// "postCreateCommand": "ruby --version",
|
|
33
16
|
|
|
34
|
-
//
|
|
35
|
-
"
|
|
36
|
-
"features": {
|
|
37
|
-
"ghcr.io/devcontainers-contrib/features/act:1": {},
|
|
38
|
-
"ghcr.io/devcontainers/features/docker-in-docker:2": {}
|
|
39
|
-
}
|
|
17
|
+
// Configure tool-specific properties.
|
|
18
|
+
// "customizations": {},
|
|
40
19
|
|
|
20
|
+
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
|
21
|
+
// "remoteUser": "root"
|
|
41
22
|
}
|
|
@@ -2,35 +2,77 @@ name: Release
|
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
4
|
push:
|
|
5
|
-
branches:
|
|
5
|
+
branches:
|
|
6
6
|
- 'main'
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
inputs:
|
|
9
|
+
tag:
|
|
10
|
+
description: 'Existing tag to build and publish (e.g. v6.3.1). Used to backfill a release that never reached RubyGems.'
|
|
11
|
+
required: false
|
|
12
|
+
type: string
|
|
13
|
+
skip_tests:
|
|
14
|
+
description: 'Skip the test suite before publishing (only for backfilling an already-tagged release).'
|
|
15
|
+
required: false
|
|
16
|
+
default: false
|
|
17
|
+
type: boolean
|
|
18
|
+
|
|
19
|
+
permissions:
|
|
20
|
+
contents: write
|
|
21
|
+
pull-requests: write
|
|
22
|
+
|
|
7
23
|
jobs:
|
|
8
|
-
release:
|
|
24
|
+
release-please:
|
|
25
|
+
if: ${{ github.event_name == 'push' }}
|
|
9
26
|
runs-on: ubuntu-latest
|
|
27
|
+
outputs:
|
|
28
|
+
release_created: ${{ steps.release.outputs.release_created }}
|
|
29
|
+
tag_name: ${{ steps.release.outputs.tag_name }}
|
|
10
30
|
steps:
|
|
11
|
-
- uses:
|
|
31
|
+
- uses: googleapis/release-please-action@v4
|
|
12
32
|
id: release
|
|
13
33
|
with:
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
34
|
+
config-file: release-please-config.json
|
|
35
|
+
manifest-file: .release-please-manifest.json
|
|
36
|
+
|
|
37
|
+
publish:
|
|
38
|
+
needs: [release-please]
|
|
39
|
+
if: ${{ always() && ((github.event_name == 'workflow_dispatch' && inputs.tag != '') || needs.release-please.outputs.release_created == 'true') }}
|
|
40
|
+
runs-on: ubuntu-latest
|
|
41
|
+
timeout-minutes: 20
|
|
42
|
+
permissions:
|
|
43
|
+
contents: write
|
|
44
|
+
id-token: write
|
|
45
|
+
env:
|
|
46
|
+
TAG: ${{ inputs.tag || needs.release-please.outputs.tag_name }}
|
|
47
|
+
steps:
|
|
48
|
+
- uses: actions/checkout@v6
|
|
49
|
+
with:
|
|
50
|
+
ref: ${{ env.TAG }}
|
|
51
|
+
fetch-depth: 0
|
|
52
|
+
persist-credentials: false
|
|
53
|
+
- name: Set up Ruby
|
|
54
|
+
uses: ruby/setup-ruby@v1
|
|
22
55
|
with:
|
|
23
|
-
ruby-version: 3.
|
|
24
|
-
bundler-cache:
|
|
25
|
-
|
|
56
|
+
ruby-version: '3.3'
|
|
57
|
+
bundler-cache: ${{ !inputs.skip_tests }}
|
|
58
|
+
- name: Verify gem version matches tag
|
|
59
|
+
run: |
|
|
60
|
+
tag_version="${TAG#v}"
|
|
61
|
+
gem_version="$(ruby -Ilib -e "require 'rotp/version'; print ROTP::VERSION")"
|
|
62
|
+
echo "tag=$tag_version gem=$gem_version"
|
|
63
|
+
test "$tag_version" = "$gem_version"
|
|
26
64
|
- name: Run tests
|
|
65
|
+
if: ${{ !inputs.skip_tests }}
|
|
27
66
|
run: bundle exec rspec
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
- name:
|
|
31
|
-
|
|
67
|
+
- name: Configure RubyGems credentials (trusted publishing)
|
|
68
|
+
uses: rubygems/configure-rubygems-credentials@v2.1.0
|
|
69
|
+
- name: Build and publish gem
|
|
70
|
+
run: |
|
|
71
|
+
gem build *.gemspec
|
|
72
|
+
gem push *.gem
|
|
73
|
+
- name: Attach gem to the GitHub release
|
|
32
74
|
env:
|
|
33
75
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
34
|
-
run:
|
|
35
|
-
|
|
36
|
-
gh release upload $
|
|
76
|
+
run: |
|
|
77
|
+
gh release view "$TAG" >/dev/null 2>&1 || gh release create "$TAG" --title "$TAG" --generate-notes
|
|
78
|
+
gh release upload "$TAG" *.gem --clobber
|
data/.github/workflows/test.yaml
CHANGED
|
@@ -4,7 +4,7 @@ on:
|
|
|
4
4
|
push:
|
|
5
5
|
branches: [ main ]
|
|
6
6
|
pull_request:
|
|
7
|
-
|
|
7
|
+
branches: [ main ]
|
|
8
8
|
|
|
9
9
|
jobs:
|
|
10
10
|
test:
|
|
@@ -13,10 +13,9 @@ jobs:
|
|
|
13
13
|
|
|
14
14
|
strategy:
|
|
15
15
|
matrix:
|
|
16
|
-
ruby-version: ['3.2', '3.0', '2.7', '2.3', truffleruby-head]
|
|
17
|
-
|
|
16
|
+
ruby-version: ['4.0', '3.4', '3.3', '3.2', '3.0', '2.7', '2.3', 'truffleruby-head']
|
|
18
17
|
steps:
|
|
19
|
-
- uses: actions/checkout@
|
|
18
|
+
- uses: actions/checkout@v6
|
|
20
19
|
- name: Set up Ruby ${{ matrix.ruby-version }}
|
|
21
20
|
uses: ruby/setup-ruby@v1
|
|
22
21
|
with:
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [6.4.0](https://github.com/mdp/rotp/compare/v6.3.1...v6.4.0) (2026-10-29)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* add skip_default_uri_params option to provisioning URIs ([16e873c](https://github.com/mdp/rotp/commit/16e873c68e0736f8ce019252985ad0e9fcce95f1))
|
|
9
|
+
|
|
10
|
+
## [6.3.1] (2025-06-03)
|
|
11
|
+
|
|
12
|
+
* Fix: Drop usage of OpenStruct to support Ruby 3.5
|
|
13
|
+
|
|
3
14
|
## [6.3.0](https://github.com/mdp/rotp/compare/v6.2.2...v6.3.0) (2023-08-30)
|
|
4
15
|
|
|
5
16
|
|
data/README.md
CHANGED
|
@@ -8,7 +8,7 @@ Although this library will continue to be maintained, if you're implementing a 2
|
|
|
8
8
|
- [Webauthn Ruby Gem](https://github.com/cedarcode/webauthn-ruby)
|
|
9
9
|
- [Rails demo app with Webauthn](https://github.com/cedarcode/webauthn-rails-demo-app)
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
---
|
|
12
12
|
|
|
13
13
|
# The Ruby One Time Password Library
|
|
14
14
|
|
|
@@ -17,7 +17,6 @@ Although this library will continue to be maintained, if you're implementing a 2
|
|
|
17
17
|
[](https://www.rubydoc.info/github/mdp/rotp/master)
|
|
18
18
|
[](https://github.com/mdp/rotp/blob/master/LICENSE)
|
|
19
19
|
|
|
20
|
-
|
|
21
20
|
A ruby library for generating and validating one time passwords (HOTP & TOTP) according to [RFC 4226](https://datatracker.ietf.org/doc/html/rfc4226) and [RFC 6238](https://datatracker.ietf.org/doc/html/rfc6238).
|
|
22
21
|
|
|
23
22
|
ROTP is compatible with [Google Authenticator](https://github.com/google/google-authenticator) available for [Android](https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2) and [iPhone](https://itunes.apple.com/en/app/google-authenticator/id388497605) and any other TOTP based implementations.
|
|
@@ -150,6 +149,10 @@ totp.provisioning_uri("alice@google.com") # => 'otpauth://totp/My%20Service:alic
|
|
|
150
149
|
|
|
151
150
|
hotp = ROTP::HOTP.new("base32secret3232", issuer: "My Service")
|
|
152
151
|
hotp.provisioning_uri("alice@google.com", 0) # => 'otpauth://hotp/My%20Service:alice%40google.com?secret=base32secret3232&issuer=My%20Service&counter=0'
|
|
152
|
+
|
|
153
|
+
# By default optional values will be skipped from URI. But you can force to specify all values in the URI by passing `skip_default_uri_params: false`
|
|
154
|
+
totp = ROTP::TOTP.new("base32secret3232", issuer: "My Service", skip_default_uri_params: false)
|
|
155
|
+
totp.provisioning_uri("alice@google.com") # => otpauth://totp/My%20Service:alice%40google.com?secret=base32secret3232&issuer=My%20Service&algorithm=SHA1&digits=6&period=30
|
|
153
156
|
```
|
|
154
157
|
|
|
155
158
|
This can then be rendered as a QR Code which the user can scan using their mobile phone and the appropriate application.
|
data/lib/rotp/arguments.rb
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
require 'optparse'
|
|
2
|
-
require 'ostruct'
|
|
3
2
|
|
|
4
3
|
module ROTP
|
|
5
4
|
class Arguments
|
|
5
|
+
Options = Struct.new(:time, :counter, :mode, :digest, :secret, :warnings)
|
|
6
|
+
|
|
6
7
|
def initialize(filename, arguments)
|
|
7
8
|
@filename = filename
|
|
8
9
|
@arguments = Array(arguments)
|
|
@@ -26,7 +27,15 @@ module ROTP
|
|
|
26
27
|
end
|
|
27
28
|
|
|
28
29
|
def default_options
|
|
29
|
-
|
|
30
|
+
o = Options.new
|
|
31
|
+
o.time = true
|
|
32
|
+
o.counter = 0
|
|
33
|
+
o.mode = :time
|
|
34
|
+
o
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def to_h
|
|
38
|
+
options!.to_h
|
|
30
39
|
end
|
|
31
40
|
|
|
32
41
|
def parse
|
data/lib/rotp/cli.rb
CHANGED
|
@@ -31,9 +31,9 @@ module ROTP
|
|
|
31
31
|
return arguments.to_s if options.mode == :help
|
|
32
32
|
|
|
33
33
|
if options.mode == :time
|
|
34
|
-
ROTP::TOTP.new(options.secret, options).now
|
|
34
|
+
ROTP::TOTP.new(options.secret, options.to_h).now
|
|
35
35
|
elsif options.mode == :hmac
|
|
36
|
-
ROTP::HOTP.new(options.secret, options).at options.counter
|
|
36
|
+
ROTP::HOTP.new(options.secret, options.to_h).at options.counter
|
|
37
37
|
end
|
|
38
38
|
end
|
|
39
39
|
|
data/lib/rotp/otp/uri.rb
CHANGED
|
@@ -15,7 +15,7 @@ module ROTP
|
|
|
15
15
|
private
|
|
16
16
|
|
|
17
17
|
def algorithm
|
|
18
|
-
return
|
|
18
|
+
return if skip_default_uri_params && @otp.digest == DEFAULT_DIGEST
|
|
19
19
|
|
|
20
20
|
@otp.digest.upcase
|
|
21
21
|
end
|
|
@@ -28,7 +28,7 @@ module ROTP
|
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
def digits
|
|
31
|
-
return if @otp.digits == DEFAULT_DIGITS
|
|
31
|
+
return if skip_default_uri_params && @otp.digits == DEFAULT_DIGITS
|
|
32
32
|
|
|
33
33
|
@otp.digits
|
|
34
34
|
end
|
|
@@ -62,7 +62,7 @@ module ROTP
|
|
|
62
62
|
|
|
63
63
|
def period
|
|
64
64
|
return if @otp.is_a?(HOTP)
|
|
65
|
-
return if @otp.interval == DEFAULT_INTERVAL
|
|
65
|
+
return if skip_default_uri_params && @otp.interval == DEFAULT_INTERVAL
|
|
66
66
|
|
|
67
67
|
@otp.interval
|
|
68
68
|
end
|
|
@@ -73,6 +73,10 @@ module ROTP
|
|
|
73
73
|
when HOTP then 'hotp'
|
|
74
74
|
end
|
|
75
75
|
end
|
|
76
|
+
|
|
77
|
+
def skip_default_uri_params
|
|
78
|
+
@otp.skip_default_uri_params
|
|
79
|
+
end
|
|
76
80
|
end
|
|
77
81
|
end
|
|
78
82
|
end
|
data/lib/rotp/otp.rb
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
module ROTP
|
|
2
2
|
class OTP
|
|
3
|
-
attr_reader :secret, :digits, :digest, :name, :issuer, :provisioning_params
|
|
3
|
+
attr_reader :secret, :digits, :digest, :name, :issuer, :provisioning_params, :skip_default_uri_params
|
|
4
4
|
DEFAULT_DIGITS = 6
|
|
5
|
+
DEFAULT_DIGEST = 'sha1'
|
|
5
6
|
|
|
6
7
|
# @param [String] secret in the form of base32
|
|
7
8
|
# @option options digits [Integer] (6)
|
|
@@ -19,13 +20,18 @@ module ROTP
|
|
|
19
20
|
# @option options provisioning_params [Hash] ({})
|
|
20
21
|
# Additional non-standard params you may want appended to the
|
|
21
22
|
# provisioning URI. Ex. `image: 'https://example.com/icon.png'`
|
|
23
|
+
# @option options skip_default_uri_params [Boolean] (true)
|
|
24
|
+
# When true, parameters that match the RFC defaults (SHA1, 6 digits,
|
|
25
|
+
# 30 second period) are omitted from the provisioning URI. Set to
|
|
26
|
+
# false to always include them.
|
|
22
27
|
# @returns [OTP] OTP instantiation
|
|
23
28
|
def initialize(s, options = {})
|
|
24
29
|
@digits = options[:digits] || DEFAULT_DIGITS
|
|
25
|
-
@digest = options[:digest] ||
|
|
30
|
+
@digest = options[:digest] || DEFAULT_DIGEST
|
|
26
31
|
@name = options[:name]
|
|
27
32
|
@issuer = options[:issuer]
|
|
28
33
|
@provisioning_params = options[:provisioning_params] || {}
|
|
34
|
+
@skip_default_uri_params = options.fetch(:skip_default_uri_params, true)
|
|
29
35
|
@secret = s
|
|
30
36
|
end
|
|
31
37
|
|
data/lib/rotp/version.rb
CHANGED
data/release-please-config.json
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
{
|
|
2
|
+
"release-type": "ruby",
|
|
2
3
|
"packages": {
|
|
3
4
|
".": {
|
|
5
|
+
"release-type": "ruby",
|
|
6
|
+
"version-file": "lib/rotp/version.rb",
|
|
4
7
|
"changelog-path": "CHANGELOG.md",
|
|
5
8
|
"bump-minor-pre-major": false,
|
|
6
9
|
"bump-patch-for-minor-pre-major": false,
|
|
@@ -9,4 +12,4 @@
|
|
|
9
12
|
}
|
|
10
13
|
},
|
|
11
14
|
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json"
|
|
12
|
-
}
|
|
15
|
+
}
|
data/spec/lib/rotp/hotp_spec.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
|
+
require 'uri'
|
|
2
3
|
|
|
3
4
|
RSpec.describe ROTP::HOTP do
|
|
4
5
|
let(:counter) { 1234 }
|
|
@@ -109,7 +110,7 @@ RSpec.describe ROTP::HOTP do
|
|
|
109
110
|
|
|
110
111
|
describe '#provisioning_uri' do
|
|
111
112
|
let(:hotp) { ROTP::HOTP.new('a' * 32, name: "m@mdp.im") }
|
|
112
|
-
let(:params) {
|
|
113
|
+
let(:params) { query_params_for(uri) }
|
|
113
114
|
|
|
114
115
|
it 'created from the otp instance data' do
|
|
115
116
|
expect(hotp.provisioning_uri())
|
|
@@ -37,6 +37,12 @@ RSpec.describe ROTP::OTP::URI do
|
|
|
37
37
|
expect(uri.to_s).to eq 'otpauth://totp/alice%40google.com?secret=JBSWY3DPEHPK3PXP&algorithm=SHA512'
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
+
it 'includes an uppercase non-default digest' do
|
|
41
|
+
otp = ROTP::TOTP.new('JBSWY3DPEHPK3PXP', digest: 'SHA256')
|
|
42
|
+
uri = described_class.new(otp, account_name: 'alice@google.com')
|
|
43
|
+
expect(uri.to_s).to eq 'otpauth://totp/alice%40google.com?secret=JBSWY3DPEHPK3PXP&algorithm=SHA256'
|
|
44
|
+
end
|
|
45
|
+
|
|
40
46
|
it 'includes non-default 8 digits' do
|
|
41
47
|
otp = ROTP::TOTP.new('JBSWY3DPEHPK3PXP', digits: 8)
|
|
42
48
|
uri = described_class.new(otp, account_name: 'alice@google.com')
|
|
@@ -96,4 +102,37 @@ RSpec.describe ROTP::OTP::URI do
|
|
|
96
102
|
uri = described_class.new(otp, account_name: 'alice+1234@google.com')
|
|
97
103
|
expect(uri.to_s).to eq 'otpauth://totp/alice%2B1234%40google.com?secret=JBSWY3DPEHPK3PXP'
|
|
98
104
|
end
|
|
105
|
+
|
|
106
|
+
context 'with skip_default_uri_params' do
|
|
107
|
+
it 'excludes default SHA1 algorithm' do
|
|
108
|
+
otp = ROTP::TOTP.new('JBSWY3DPEHPK3PXP', digest: 'sha1', skip_default_uri_params: true)
|
|
109
|
+
uri = described_class.new(otp, account_name: 'alice@google.com')
|
|
110
|
+
expect(uri.to_s).to eq 'otpauth://totp/alice%40google.com?secret=JBSWY3DPEHPK3PXP'
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
it 'excludes default 6 digits' do
|
|
114
|
+
otp = ROTP::TOTP.new('JBSWY3DPEHPK3PXP', digits: 6, skip_default_uri_params: true)
|
|
115
|
+
uri = described_class.new(otp, account_name: 'alice@google.com')
|
|
116
|
+
expect(uri.to_s).to eq 'otpauth://totp/alice%40google.com?secret=JBSWY3DPEHPK3PXP'
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
it 'excludes default 30 second period' do
|
|
120
|
+
otp = ROTP::TOTP.new('JBSWY3DPEHPK3PXP', interval: 30, skip_default_uri_params: true)
|
|
121
|
+
uri = described_class.new(otp, account_name: 'alice@google.com')
|
|
122
|
+
expect(uri.to_s).to eq 'otpauth://totp/alice%40google.com?secret=JBSWY3DPEHPK3PXP'
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
it 'includes all parameters when skip_default_uri_params is false' do
|
|
126
|
+
otp = ROTP::TOTP.new(
|
|
127
|
+
'JBSWY3DPEHPK3PXP',
|
|
128
|
+
digest: 'sha1',
|
|
129
|
+
digits: 6,
|
|
130
|
+
interval: 30,
|
|
131
|
+
issuer: 'ACME Co',
|
|
132
|
+
skip_default_uri_params: false
|
|
133
|
+
)
|
|
134
|
+
uri = described_class.new(otp, account_name: 'alice@google.com')
|
|
135
|
+
expect(uri.to_s).to eq 'otpauth://totp/ACME%20Co:alice%40google.com?secret=JBSWY3DPEHPK3PXP&issuer=ACME%20Co&algorithm=SHA1&digits=6&period=30'
|
|
136
|
+
end
|
|
137
|
+
end
|
|
99
138
|
end
|
data/spec/lib/rotp/totp_spec.rb
CHANGED
|
@@ -223,7 +223,7 @@ RSpec.describe ROTP::TOTP do
|
|
|
223
223
|
|
|
224
224
|
|
|
225
225
|
describe '#provisioning_uri' do
|
|
226
|
-
let(:params) {
|
|
226
|
+
let(:params) { query_params_for(uri) }
|
|
227
227
|
|
|
228
228
|
context "with a provided name on the TOTP instance" do
|
|
229
229
|
let(:totp) { ROTP::TOTP.new(TEST_SECRET, name: "m@mdp.im") }
|
data/spec/spec_helper.rb
CHANGED
|
@@ -3,6 +3,7 @@ SimpleCov.start do
|
|
|
3
3
|
add_filter '/spec/'
|
|
4
4
|
end
|
|
5
5
|
|
|
6
|
+
require 'uri'
|
|
6
7
|
require 'rotp'
|
|
7
8
|
require 'timecop'
|
|
8
9
|
|
|
@@ -17,4 +18,10 @@ RSpec.configure do |config|
|
|
|
17
18
|
end
|
|
18
19
|
end
|
|
19
20
|
|
|
21
|
+
def query_params_for(uri)
|
|
22
|
+
URI.decode_www_form(URI.parse(uri).query).each_with_object(Hash.new { |hash, key| hash[key] = [] }) do |(key, value), params|
|
|
23
|
+
params[key] << value
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
20
27
|
require_relative '../lib/rotp'
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rotp
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 6.
|
|
4
|
+
version: 6.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mark Percival
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-10-29 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rake
|
|
@@ -74,9 +74,9 @@ executables:
|
|
|
74
74
|
extensions: []
|
|
75
75
|
extra_rdoc_files: []
|
|
76
76
|
files:
|
|
77
|
-
- ".devcontainer/Dockerfile"
|
|
78
77
|
- ".devcontainer/devcontainer.json"
|
|
79
78
|
- ".dockerignore"
|
|
79
|
+
- ".github/dependabot.yml"
|
|
80
80
|
- ".github/workflows/release.yaml"
|
|
81
81
|
- ".github/workflows/test.yaml"
|
|
82
82
|
- ".gitignore"
|
|
@@ -113,7 +113,7 @@ homepage: https://github.com/mdp/rotp
|
|
|
113
113
|
licenses:
|
|
114
114
|
- MIT
|
|
115
115
|
metadata: {}
|
|
116
|
-
post_install_message:
|
|
116
|
+
post_install_message:
|
|
117
117
|
rdoc_options: []
|
|
118
118
|
require_paths:
|
|
119
119
|
- lib
|
|
@@ -128,8 +128,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
128
128
|
- !ruby/object:Gem::Version
|
|
129
129
|
version: '0'
|
|
130
130
|
requirements: []
|
|
131
|
-
rubygems_version: 3.
|
|
132
|
-
signing_key:
|
|
131
|
+
rubygems_version: 3.5.22
|
|
132
|
+
signing_key:
|
|
133
133
|
specification_version: 4
|
|
134
134
|
summary: A Ruby library for generating and verifying one time passwords
|
|
135
135
|
test_files: []
|
data/.devcontainer/Dockerfile
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.238.1/containers/ruby/.devcontainer/base.Dockerfile
|
|
2
|
-
|
|
3
|
-
# [Choice] Ruby version (use -bullseye variants on local arm64/Apple Silicon): 3, 3.1, 3.0, 2, 2.7, 3-bullseye, 3.1-bullseye, 3.0-bullseye, 2-bullseye, 2.7-bullseye, 3-buster, 3.1-buster, 3.0-buster, 2-buster, 2.7-buster
|
|
4
|
-
ARG VARIANT="3.1-bullseye"
|
|
5
|
-
FROM mcr.microsoft.com/vscode/devcontainers/ruby:0-${VARIANT}
|
|
6
|
-
|
|
7
|
-
# [Choice] Node.js version: none, lts/*, 16, 14, 12, 10
|
|
8
|
-
ARG NODE_VERSION="none"
|
|
9
|
-
RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi
|
|
10
|
-
|
|
11
|
-
# [Optional] Uncomment this section to install additional OS packages.
|
|
12
|
-
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
|
|
13
|
-
# && apt-get -y install --no-install-recommends <your-package-list-here>
|
|
14
|
-
|
|
15
|
-
# [Optional] Uncomment this line to install additional gems.
|
|
16
|
-
# RUN gem install <your-gem-names-here>
|
|
17
|
-
|
|
18
|
-
# [Optional] Uncomment this line to install global node packages.
|
|
19
|
-
# RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g <your-package-here>" 2>&1
|