Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Migrate to MyMLH API v4 #13

Closed
wants to merge 1 commit into from
Closed
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
10 changes: 9 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
language: ruby
rvm:
- 2.2.0
- 3.2.0
- 3.2.2
- 3.3.0
cache: bundler
before_install:
- gem install bundler
script:
- bundle exec rake
- bundle exec rubocop
42 changes: 42 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [2.0.0] - 2024-01-06

### Added
- Support for MyMLH API v4
- Refresh token support via `offline_access` scope
- Expandable fields support in API requests
- Enhanced error handling with detailed error messages
- New granular scopes:
- `user:read:profile`
- `user:read:email`
- `user:read:demographics`
- `user:read:education`
- `user:read:employment`

### Changed
- **BREAKING**: Minimum Ruby version requirement increased to 3.2.0
- **BREAKING**: Updated scope format to match v4 API (e.g., `user:read:profile` instead of `default`)
- **BREAKING**: Changed user data endpoint from `/api/v3/user.json` to `/v4/users/me`
- **BREAKING**: Updated user data structure to match v4 API response format
- Updated development dependencies to latest stable versions
- Enhanced test coverage for v4 API features

### Removed
- **BREAKING**: Removed support for v3 API scopes
- **BREAKING**: Removed support for Ruby versions below 3.2.0

### Fixed
- Improved error handling for API request failures
- Updated documentation to reflect v4 API changes

### Security
- Updated minimum Ruby version to ensure security patches
- Updated all dependencies to their latest secure versions

[2.0.0]: https://github.com/MLH/omniauth-mlh/compare/v1.0.1...v2.0.0
22 changes: 17 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ This is the official [OmniAuth](https://github.com/omniauth/omniauth) strategy f
authenticating with [MyMLH](https://my.mlh.io). To use it, you'll need to
[register an application](https://my.mlh.io/oauth/applications) and obtain a OAuth Application ID and Secret from MyMLH.

It now supports MyMLH API V3. [Read the MyMLH V3 docs here](https://my.mlh.io/docs).
It supports MyMLH API V4. [Read the MyMLH V4 docs here](https://my.mlh.io/docs).

Once you have done so, you can follow the instructions below:

## Requirements

This Gem requires your Ruby version to be at least `2.2.0`, which is set
downstream by [Omniauth](https://github.com/omniauth/omniauth/blob/master/omniauth.gemspec#L22).
This Gem requires Ruby version 3.2.0 or higher. This requirement is set to ensure compatibility
with the latest features and security updates.

## Installation

Expand All @@ -36,7 +36,8 @@ Or install it yourself as:

```ruby
use OmniAuth::Builder do
provider :mlh, ENV['MY_MLH_KEY'], ENV['MY_MLH_SECRET'], scope: 'default email birthday'
provider :mlh, ENV['MY_MLH_KEY'], ENV['MY_MLH_SECRET'],
scope: 'user:read:profile user:read:email offline_access'
end
```

Expand All @@ -46,10 +47,21 @@ end
# config/devise.rb

Devise.setup do |config|
config.provider :mlh, ENV['MY_MLH_KEY'], ENV['MY_MLH_SECRET'], scope: 'default email birthday'
config.provider :mlh, ENV['MY_MLH_KEY'], ENV['MY_MLH_SECRET'],
scope: 'user:read:profile user:read:email offline_access'
end
```

## Available Scopes

The following scopes are available in the v4 API:
- `user:read:profile` - Access to basic profile information
- `user:read:email` - Access to email address
- `user:read:demographics` - Access to demographic information
- `user:read:education` - Access to education details
- `user:read:employment` - Access to employment information
- `offline_access` - Enables refresh token support

## Contributing

For guidance on setting up a development environment and how to make a contribution to omniauth-mlh, see the [contributing guidelines](https://github.com/MLH/omniauth-mlh/blob/main/CONTRIBUTING.md).
Expand Down
2 changes: 1 addition & 1 deletion lib/omniauth-mlh/version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

module OmniAuth
module MLH
VERSION = '1.0.1'
VERSION = '2.0.0'
end
end
83 changes: 59 additions & 24 deletions lib/omniauth/strategies/mlh.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

module OmniAuth
module Strategies
class MLH < OmniAuth::Strategies::OAuth2 # :nodoc:
class MLH < OmniAuth::Strategies::OAuth2
option :name, :mlh

option :client_options, {
Expand All @@ -14,33 +14,68 @@ class MLH < OmniAuth::Strategies::OAuth2 # :nodoc:
token_url: 'oauth/token'
}

uid { data[:id] }
# Default scope includes user:read:profile and offline_access for refresh tokens
option :scope, 'user:read:profile offline_access'

# Support for expandable fields in the API
option :fields, []

uid { raw_info['id'] }

info do
data.slice(
:email,
:created_at,
:updated_at,
:first_name,
:last_name,
:level_of_study,
:major,
:date_of_birth,
:gender,
:phone_number,
:profession_type,
:company_name,
:company_title,
:scopes,
:school
)
prune!({
'email' => raw_info.dig('email'),
'first_name' => raw_info.dig('first_name'),
'last_name' => raw_info.dig('last_name'),
'created_at' => raw_info.dig('created_at'),
'updated_at' => raw_info.dig('updated_at'),
'roles' => raw_info.dig('roles'),
'phone_number' => raw_info.dig('phone_number'),
'demographics' => {
'gender' => raw_info.dig('demographics', 'gender'),
'date_of_birth' => raw_info.dig('demographics', 'date_of_birth')
},
'education' => {
'level' => raw_info.dig('education', 'level'),
'major' => raw_info.dig('education', 'major'),
'school' => raw_info.dig('education', 'school')
},
'employment' => {
'type' => raw_info.dig('employment', 'type'),
'company' => raw_info.dig('employment', 'company'),
'title' => raw_info.dig('employment', 'title')
}
})
end

credentials do
hash = { 'token' => access_token.token }
hash['refresh_token'] = access_token.refresh_token if access_token.refresh_token
hash['expires_at'] = access_token.expires_at if access_token.expires_at
hash['expires'] = access_token.expires?
hash
end

def raw_info
@raw_info ||= begin
path = '/v4/users/me'
path += "?fields=#{options.fields.join(',')}" if options.fields.any?

response = access_token.get(path)
raise OmniAuth::Error.new(response.error) unless response.success?

response.parsed
rescue StandardError => e
raise OmniAuth::Error.new("Failed to get user info: #{e.message}")
end
end

def data
@data ||= begin
access_token.get('/api/v3/user.json').parsed.deep_symbolize_keys[:data]
rescue StandardError
{}
private

def prune!(hash)
hash.delete_if do |_, value|
prune!(value) if value.is_a?(Hash)
value.nil? || (value.respond_to?(:empty?) && value.empty?)
end
end
end
Expand Down
20 changes: 10 additions & 10 deletions omniauth-mlh.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ Gem::Specification.new do |spec|
spec.email = ['[email protected]']

spec.summary = 'Official OmniAuth strategy for MyMLH.'
spec.description = 'Official OmniAuth strategy for MyMLH.'
spec.description = 'Official OmniAuth strategy for MyMLH v4 API.'
spec.homepage = 'http://github.com/mlh/omniauth-mlh'
spec.license = 'MIT'

spec.required_ruby_version = '>= 2.7.0'
spec.required_ruby_version = '>= 3.2.0'

spec.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
spec.files = `git ls-files`.split("\n")
Expand All @@ -27,12 +27,12 @@ Gem::Specification.new do |spec|
spec.add_dependency 'omniauth', '~> 2.1.1'
spec.add_dependency 'omniauth-oauth2', '~> 1.8.0'

spec.add_development_dependency 'rack-test'
spec.add_development_dependency 'rake', '~> 12.3.3'
spec.add_development_dependency 'rspec', '~> 3.10'
spec.add_development_dependency 'rubocop', '~> 1.0'
spec.add_development_dependency 'rubocop-performance'
spec.add_development_dependency 'rubocop-rspec'
spec.add_development_dependency 'simplecov'
spec.add_development_dependency 'webmock'
spec.add_development_dependency 'rack-test', '~> 2.1'
spec.add_development_dependency 'rake', '~> 13.1'
spec.add_development_dependency 'rspec', '~> 3.12'
spec.add_development_dependency 'rubocop', '~> 1.57'
spec.add_development_dependency 'rubocop-performance', '~> 1.19'
spec.add_development_dependency 'rubocop-rspec', '~> 2.24'
spec.add_development_dependency 'simplecov', '~> 0.22'
spec.add_development_dependency 'webmock', '~> 3.19'
end
Loading
Loading