unmagic-passkeys 0.1.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.
Files changed (36) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +9 -0
  3. data/LICENSE +21 -0
  4. data/NOTICE +9 -0
  5. data/README.md +151 -0
  6. data/app/assets/javascripts/unmagic/passkeys/passkey.js +236 -0
  7. data/app/assets/javascripts/unmagic/passkeys/webauthn.js +83 -0
  8. data/app/controllers/unmagic/passkeys/challenges_controller.rb +49 -0
  9. data/app/models/unmagic/passkeys/credential.rb +103 -0
  10. data/config/importmap.rb +5 -0
  11. data/config/routes.rb +2 -0
  12. data/lib/generators/unmagic/passkeys/install_generator.rb +51 -0
  13. data/lib/generators/unmagic/passkeys/templates/POST_INSTALL +19 -0
  14. data/lib/generators/unmagic/passkeys/templates/create_unmagic_passkeys_credentials.rb.tt +19 -0
  15. data/lib/unmagic/passkeys/engine.rb +78 -0
  16. data/lib/unmagic/passkeys/form_helper.rb +128 -0
  17. data/lib/unmagic/passkeys/holder.rb +143 -0
  18. data/lib/unmagic/passkeys/request.rb +77 -0
  19. data/lib/unmagic/passkeys/version.rb +5 -0
  20. data/lib/unmagic/passkeys/web_authn/authenticator/assertion_response.rb +88 -0
  21. data/lib/unmagic/passkeys/web_authn/authenticator/attestation.rb +73 -0
  22. data/lib/unmagic/passkeys/web_authn/authenticator/attestation_response.rb +71 -0
  23. data/lib/unmagic/passkeys/web_authn/authenticator/attestation_verifiers/none.rb +24 -0
  24. data/lib/unmagic/passkeys/web_authn/authenticator/data.rb +174 -0
  25. data/lib/unmagic/passkeys/web_authn/authenticator/response.rb +141 -0
  26. data/lib/unmagic/passkeys/web_authn/cbor_decoder.rb +269 -0
  27. data/lib/unmagic/passkeys/web_authn/cose_key.rb +183 -0
  28. data/lib/unmagic/passkeys/web_authn/current.rb +19 -0
  29. data/lib/unmagic/passkeys/web_authn/public_key_credential/creation_options.rb +109 -0
  30. data/lib/unmagic/passkeys/web_authn/public_key_credential/options.rb +80 -0
  31. data/lib/unmagic/passkeys/web_authn/public_key_credential/request_options.rb +55 -0
  32. data/lib/unmagic/passkeys/web_authn/public_key_credential.rb +153 -0
  33. data/lib/unmagic/passkeys/web_authn/relying_party.rb +50 -0
  34. data/lib/unmagic/passkeys/web_authn.rb +84 -0
  35. data/lib/unmagic/passkeys.rb +41 -0
  36. metadata +152 -0
@@ -0,0 +1,84 @@
1
+ # = Action Pack WebAuthn
2
+ #
3
+ # Provides a pure-Ruby implementation of the WebAuthn (Web Authentication)
4
+ # specification for passkey registration and authentication. This module
5
+ # is the top-level namespace for all WebAuthn components and provides
6
+ # shared utilities used across ceremonies.
7
+ #
8
+ # == Components
9
+ #
10
+ # [Unmagic::Passkeys::WebAuthn::RelyingParty]
11
+ # Identifies your application to authenticators.
12
+ #
13
+ # [Unmagic::Passkeys::WebAuthn::PublicKeyCredential]
14
+ # Orchestrates registration and authentication ceremonies.
15
+ #
16
+ # [Unmagic::Passkeys::WebAuthn::Authenticator]
17
+ # Parses and validates authenticator responses.
18
+ #
19
+ # [Unmagic::Passkeys::WebAuthn::CborDecoder]
20
+ # Decodes CBOR-encoded data from authenticators.
21
+ #
22
+ # [Unmagic::Passkeys::WebAuthn::CoseKey]
23
+ # Parses COSE public keys into OpenSSL key objects.
24
+ #
25
+ # == Extending Attestation Formats
26
+ #
27
+ # By default only the "none" attestation format is supported. Register
28
+ # additional verifiers with:
29
+ #
30
+ # Unmagic::Passkeys::WebAuthn.register_attestation_verifier("packed", MyPackedVerifier.new)
31
+ #
32
+ module Unmagic::Passkeys::WebAuthn
33
+ class InvalidResponseError < StandardError; end
34
+ class InvalidCborError < StandardError; end
35
+ class InvalidKeyError < StandardError; end
36
+ class UnsupportedKeyTypeError < StandardError; end
37
+ class InvalidOptionsError < StandardError; end
38
+
39
+ class << self
40
+ # Returns a new RelyingParty configured from the current request context.
41
+ def relying_party
42
+ RelyingParty.new
43
+ end
44
+
45
+ # Returns the MessageVerifier used to sign and verify WebAuthn challenges.
46
+ def challenge_verifier
47
+ Rails.application.message_verifier("action_pack.webauthn.challenge")
48
+ end
49
+
50
+ # Returns the registry of attestation format verifiers, keyed by format
51
+ # string (e.g., "none", "packed"). Only "none" is registered by default.
52
+ def attestation_verifiers
53
+ @attestation_verifiers ||= {
54
+ "none" => Authenticator::AttestationVerifiers::None.new
55
+ }
56
+ end
57
+
58
+ # Registers a custom attestation verifier for the given +format+.
59
+ # The +verifier+ must respond to +verify!(attestation, client_data_json:)+.
60
+ def register_attestation_verifier(format, verifier)
61
+ attestation_verifiers[format.to_s] = verifier
62
+ end
63
+ end
64
+
65
+ # Implicit namespaces for the ceremony files required below (no Zeitwerk in lib/).
66
+ module Authenticator
67
+ module AttestationVerifiers; end
68
+ end
69
+ end
70
+
71
+ require_relative "web_authn/current"
72
+ require_relative "web_authn/relying_party"
73
+ require_relative "web_authn/cbor_decoder"
74
+ require_relative "web_authn/cose_key"
75
+ require_relative "web_authn/public_key_credential"
76
+ require_relative "web_authn/public_key_credential/options"
77
+ require_relative "web_authn/public_key_credential/creation_options"
78
+ require_relative "web_authn/public_key_credential/request_options"
79
+ require_relative "web_authn/authenticator/response"
80
+ require_relative "web_authn/authenticator/data"
81
+ require_relative "web_authn/authenticator/attestation"
82
+ require_relative "web_authn/authenticator/attestation_verifiers/none"
83
+ require_relative "web_authn/authenticator/attestation_response"
84
+ require_relative "web_authn/authenticator/assertion_response"
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support"
4
+ require "active_support/all"
5
+ require "active_model"
6
+ require "openssl"
7
+ require "base64"
8
+ require "json"
9
+ require "digest"
10
+
11
+ require "unmagic/passkeys/version"
12
+ require "unmagic/passkeys/web_authn"
13
+ require "unmagic/passkeys/holder"
14
+ require "unmagic/passkeys/request"
15
+ require "unmagic/passkeys/form_helper"
16
+ require "unmagic/passkeys/engine" if defined?(::Rails::Engine)
17
+
18
+ module Unmagic
19
+ # Passkey (WebAuthn) authentication for Rails. The module methods are thin delegators to the
20
+ # +Unmagic::Passkeys::Credential+ Active Record model so host code reads as
21
+ # +Unmagic::Passkeys.authenticate(params)+.
22
+ module Passkeys
23
+ class << self
24
+ def registration_options(**options)
25
+ Credential.registration_options(**options)
26
+ end
27
+
28
+ def register(passkey, **attributes)
29
+ Credential.register(passkey, **attributes)
30
+ end
31
+
32
+ def authentication_options(**options)
33
+ Credential.authentication_options(**options)
34
+ end
35
+
36
+ def authenticate(passkey)
37
+ Credential.authenticate(passkey)
38
+ end
39
+ end
40
+ end
41
+ end
metadata ADDED
@@ -0,0 +1,152 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: unmagic-passkeys
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Keith Pitt
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-06-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '7.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '7.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: actionpack
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '7.1'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '7.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: actionview
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '7.1'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '7.1'
55
+ - !ruby/object:Gem::Dependency
56
+ name: activesupport
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '7.1'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '7.1'
69
+ - !ruby/object:Gem::Dependency
70
+ name: railties
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '7.1'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '7.1'
83
+ description: A Rails engine that adds passkey registration and authentication backed
84
+ by Active Record. Self-contained, pure-Ruby WebAuthn ceremonies (CBOR, COSE, attestation
85
+ and assertion verification) with stateless signed challenges — no external gems.
86
+ email:
87
+ - keith@unreasonable-magic.com
88
+ executables: []
89
+ extensions: []
90
+ extra_rdoc_files: []
91
+ files:
92
+ - CHANGELOG.md
93
+ - LICENSE
94
+ - NOTICE
95
+ - README.md
96
+ - app/assets/javascripts/unmagic/passkeys/passkey.js
97
+ - app/assets/javascripts/unmagic/passkeys/webauthn.js
98
+ - app/controllers/unmagic/passkeys/challenges_controller.rb
99
+ - app/models/unmagic/passkeys/credential.rb
100
+ - config/importmap.rb
101
+ - config/routes.rb
102
+ - lib/generators/unmagic/passkeys/install_generator.rb
103
+ - lib/generators/unmagic/passkeys/templates/POST_INSTALL
104
+ - lib/generators/unmagic/passkeys/templates/create_unmagic_passkeys_credentials.rb.tt
105
+ - lib/unmagic/passkeys.rb
106
+ - lib/unmagic/passkeys/engine.rb
107
+ - lib/unmagic/passkeys/form_helper.rb
108
+ - lib/unmagic/passkeys/holder.rb
109
+ - lib/unmagic/passkeys/request.rb
110
+ - lib/unmagic/passkeys/version.rb
111
+ - lib/unmagic/passkeys/web_authn.rb
112
+ - lib/unmagic/passkeys/web_authn/authenticator/assertion_response.rb
113
+ - lib/unmagic/passkeys/web_authn/authenticator/attestation.rb
114
+ - lib/unmagic/passkeys/web_authn/authenticator/attestation_response.rb
115
+ - lib/unmagic/passkeys/web_authn/authenticator/attestation_verifiers/none.rb
116
+ - lib/unmagic/passkeys/web_authn/authenticator/data.rb
117
+ - lib/unmagic/passkeys/web_authn/authenticator/response.rb
118
+ - lib/unmagic/passkeys/web_authn/cbor_decoder.rb
119
+ - lib/unmagic/passkeys/web_authn/cose_key.rb
120
+ - lib/unmagic/passkeys/web_authn/current.rb
121
+ - lib/unmagic/passkeys/web_authn/public_key_credential.rb
122
+ - lib/unmagic/passkeys/web_authn/public_key_credential/creation_options.rb
123
+ - lib/unmagic/passkeys/web_authn/public_key_credential/options.rb
124
+ - lib/unmagic/passkeys/web_authn/public_key_credential/request_options.rb
125
+ - lib/unmagic/passkeys/web_authn/relying_party.rb
126
+ homepage: https://github.com/unreasonable-magic/unmagic-passkeys
127
+ licenses:
128
+ - MIT
129
+ metadata:
130
+ homepage_uri: https://github.com/unreasonable-magic/unmagic-passkeys
131
+ source_code_uri: https://github.com/unreasonable-magic/unmagic-passkeys
132
+ changelog_uri: https://github.com/unreasonable-magic/unmagic-passkeys/blob/main/CHANGELOG.md
133
+ post_install_message:
134
+ rdoc_options: []
135
+ require_paths:
136
+ - lib
137
+ required_ruby_version: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - ">="
140
+ - !ruby/object:Gem::Version
141
+ version: '3.2'
142
+ required_rubygems_version: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ version: '0'
147
+ requirements: []
148
+ rubygems_version: 3.5.22
149
+ signing_key:
150
+ specification_version: 4
151
+ summary: Passkey (WebAuthn) authentication for Rails, with no external dependencies
152
+ test_files: []