Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
/pkg/
/spec/reports/
/tmp/
*.gem
24 changes: 24 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,27 @@ Style/StringLiterals:

Style/StringLiteralsInInterpolation:
EnforcedStyle: double_quotes

Layout/FirstHashElementIndentation:
EnforcedStyle: consistent

Style/TrailingCommaInHashLiteral:
EnforcedStyleForMultiline: comma

Metrics/BlockLength:
Max: 60

Layout/FirstArrayElementIndentation:
EnforcedStyle: consistent

Style/TrailingCommaInArrayLiteral:
EnforcedStyleForMultiline: comma

Style/TrailingCommaInArguments:
EnforcedStyleForMultiline: comma

Style/StringConcatenation:
Enabled: false

Layout/MultilineOperationIndentation:
Enabled: false
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ gem "rake", "~> 13.0"

gem "minitest", "~> 5.16"

gem "debug"
gem "rubocop", "~> 1.21"
85 changes: 85 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
PATH
remote: .
specs:
vauth (0.1.0.alpha1)
jwt (~> 3.1, >= 3.1.2)
zeitwerk (~> 2.7, >= 2.7.4)

GEM
remote: https://rubygems.org/
specs:
ast (2.4.3)
base64 (0.3.0)
date (3.5.1)
debug (1.11.1)
irb (~> 1.10)
reline (>= 0.3.8)
erb (6.0.1)
io-console (0.8.2)
irb (1.16.0)
pp (>= 0.6.0)
rdoc (>= 4.0.0)
reline (>= 0.4.2)
json (2.18.0)
jwt (3.1.2)
base64
language_server-protocol (3.17.0.5)
lint_roller (1.1.0)
minitest (5.27.0)
parallel (1.27.0)
parser (3.3.10.0)
ast (~> 2.4.1)
racc
pp (0.6.3)
prettyprint
prettyprint (0.2.0)
prism (1.7.0)
psych (5.3.1)
date
stringio
racc (1.8.1)
rainbow (3.1.1)
rake (13.3.1)
rdoc (7.0.3)
erb
psych (>= 4.0.0)
tsort
regexp_parser (2.11.3)
reline (0.6.3)
io-console (~> 0.5)
rubocop (1.82.1)
json (~> 2.3)
language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.1.0)
parallel (~> 1.10)
parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 2.9.3, < 3.0)
rubocop-ast (>= 1.48.0, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 4.0)
rubocop-ast (1.49.0)
parser (>= 3.3.7.2)
prism (~> 1.7)
ruby-progressbar (1.13.0)
stringio (3.2.0)
tsort (0.2.0)
unicode-display_width (3.2.0)
unicode-emoji (~> 4.1)
unicode-emoji (4.2.0)
zeitwerk (2.7.4)

PLATFORMS
ruby
x86_64-linux

DEPENDENCIES
debug
irb
minitest (~> 5.16)
rake (~> 13.0)
rubocop (~> 1.21)
vauth!

BUNDLED WITH
2.6.7
51 changes: 46 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# Vauth

TODO: Delete this and the text below, and describe your gem
> [!WARNING]
> Do not use this in production. This gem is still in the pre-release phase. The API might change without any notice.

Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/vauth`. To experiment with that code, run `bin/console` for an interactive prompt.
A collection of Ruby objects that can be stitched together to create an OAuth 2.0 flow. Currently only supports the
Authorization Code Grant flow.

## Installation

Expand All @@ -22,7 +24,46 @@ gem install UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG

## Usage

TODO: Write usage instructions here
Create a Client:

```rb
client = ::Vauth::Client.new(
"client-id-from-oauth2-provider",
"client-secret-from-oauth2-provider",
"https://oauth2-provider.com/authorization-uri",
"https://oauth2-provider.com/token-uri",
)
```

Pass it to create an Authorization Request:

```rb
request = ::Vauth::AuthorizationRequest.new(client)
```

Store the request state via `request.state` to a secure storage, then redirect the Resource Owner (user) via
`request.url` to authorize the application.

When the Resource Owner gets redirected back, recreate the Authorization Request from the stored state:

```rb
request = ::Vauth::AuthorizationRequest.new(client, stored_state)
```

Then you should be able to get the Authorization Code Grant with the re-created request, the received code, and the
received state:

```rb
grant = ::Vauth::AuthorizationCodeGrant.new(request, code, received_state)
```

The grant will give you the ID Token:

```rb
token = grant.identity_token
```

The ID Token will give you the issuer via `token.issuer`, and the subject via `token.subject`.

## Development

Expand All @@ -32,12 +73,12 @@ To install this gem onto your local machine, run `bundle exec rake install`. To

## Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/vauth. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/vauth/blob/master/CODE_OF_CONDUCT.md).
Bug reports and pull requests are welcome on GitHub at https://github.com/radiantshaw/vauth-ruby. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/radiantshaw/vauth-ruby/blob/master/CODE_OF_CONDUCT.md).

## License

The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).

## Code of Conduct

Everyone interacting in the Vauth project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/vauth/blob/master/CODE_OF_CONDUCT.md).
Everyone interacting in the Vauth project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/radiantshaw/vauth-ruby/blob/master/CODE_OF_CONDUCT.md).
27 changes: 27 additions & 0 deletions bin/rake
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

#
# This file was generated by Bundler.
#
# The application 'rake' is installed as part of a gem, and
# this file is here to facilitate running it.
#

ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)

bundle_binstub = File.expand_path("bundle", __dir__)

if File.file?(bundle_binstub)
if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
load(bundle_binstub)
else
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
end
end

require "rubygems"
require "bundler/setup"

load Gem.bin_path("rake", "rake")
7 changes: 4 additions & 3 deletions lib/vauth.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# frozen_string_literal: true

require_relative "vauth/version"
require "zeitwerk"

Zeitwerk::Loader.for_gem.setup

module Vauth
class Error < StandardError; end
# Your code goes here...
class StateMismatchError < StandardError; end
end
42 changes: 42 additions & 0 deletions lib/vauth/authorization_code_grant.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# frozen_string_literal: true

require "json"
require "net/http"

module Vauth
class AuthorizationCodeGrant # :nodoc:
def initialize(request, code, state)
@request = request
@code = code

verify_state!(request.state, state)
end

def identity_token
::Vauth::IdentityToken.new(parsed_response["id_token"])
end

private

attr_reader :request, :code

def client
request.client
end

def parsed_response
@parsed_response = JSON.parse(
::Net::HTTP.post_form(client.token_uri, {
client_id: client.id,
client_secret: client.secret,
scope: "openid",
code: code,
}).body,
)
end

def verify_state!(sent, received)
raise ::Vauth::StateMismatchError, "Cannot grant access due to state mismatch!" unless sent == received
end
end
end
36 changes: 36 additions & 0 deletions lib/vauth/authorization_request.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# frozen_string_literal: true

require "uri"

module Vauth
class AuthorizationRequest # :nodoc:
def initialize(client, state = nil)
@client = client
@state = state
end

attr_reader :client

def url(redirect_to:)
authorization_uri.query = URI.encode_www_form([
%w[response_type code],
["client_id", client.id],
["redirect_uri", redirect_to],
%w[scope openid],
["state", state],
])

String(authorization_uri)
end

def state
@state ||= SecureRandom.hex(16)
end

private

def authorization_uri
@authorization_uri ||= client.authorization_uri
end
end
end
15 changes: 15 additions & 0 deletions lib/vauth/client.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# frozen_string_literal: true

require "uri"

module Vauth
Client = Struct.new(:id, :secret, :authorization_uri, :token_uri) do
def authorization_uri
URI(self[:authorization_uri])
end

def token_uri
URI(self[:token_uri])
end
end
end
23 changes: 23 additions & 0 deletions lib/vauth/identity_token.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# frozen_string_literal: true

require "jwt"

module Vauth
class IdentityToken # :nodoc:
def initialize(jwt_id_token)
@jwt_id_token = jwt_id_token
end

def issuer
JWT.decode(jwt_id_token, nil, true, { algorithm: "none" })[0]["iss"]
end

def subject
JWT.decode(jwt_id_token, nil, true, { algorithm: "none" })[0]["sub"]
end

private

attr_reader :jwt_id_token
end
end
2 changes: 1 addition & 1 deletion lib/vauth/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module Vauth
VERSION = "0.1.0"
VERSION = "0.1.0.alpha1"
end
5 changes: 5 additions & 0 deletions test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,8 @@
require "vauth"

require "minitest/autorun"

require "json"
require "jwt"
require "net/http"
require "uri"
4 changes: 0 additions & 4 deletions test/test_vauth.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,4 @@ class TestVauth < Minitest::Test
def test_that_it_has_a_version_number
refute_nil ::Vauth::VERSION
end

def test_it_does_something_useful
assert false
end
end
Loading