omniauth-auth0-2.0.0/0000755000175000017500000000000013256144763013413 5ustar pravipraviomniauth-auth0-2.0.0/examples/0000755000175000017500000000000013256144763015231 5ustar pravipraviomniauth-auth0-2.0.0/examples/sinatra/0000755000175000017500000000000013256144763016672 5ustar pravipraviomniauth-auth0-2.0.0/examples/sinatra/app.rb0000644000175000017500000000043413256144763020000 0ustar pravipravirequire 'sinatra' require 'omniauth-auth0' require 'dotenv/load' use OmniAuth::Builder do provider :auth0, ENV['CLIENT_ID'], ENV['CLIENT_SECRET'], ENV['DOMAIN'] end enable :sessions set :session_secret, ENV['SESSION_SECRET'] get '/' do 'Auth0 OmniAuth Example for Sinatra' end omniauth-auth0-2.0.0/examples/sinatra/config.ru0000644000175000017500000000012213256144763020502 0ustar pravipravirequire File.expand_path('app', File.dirname(__FILE__)) run Sinatra::Application omniauth-auth0-2.0.0/omniauth-auth0.gemspec0000644000175000017500000000232413256144763017624 0ustar pravipravi# -*- encoding: utf-8 -*- $LOAD_PATH.push File.expand_path('../lib', __FILE__) require 'omniauth-auth0/version' Gem::Specification.new do |s| s.name = 'omniauth-auth0' s.version = OmniAuth::Auth0::VERSION s.authors = ['Auth0'] s.email = ['info@auth0.com'] s.homepage = 'https://github.com/auth0/omniauth-auth0' s.summary = 'Omniauth OAuth2 strategy for the Auth0 platform.' s.description = %q{Auth0 is an authentication broker that supports social identity providers as well as enterprise identity providers such as Active Directory, LDAP, Google Apps, Salesforce. OmniAuth is a library that standardizes multi-provider authentication for web applications. It was created to be powerful, flexible, and do as little as possible. omniauth-auth0 is the omniauth strategy for Auth0. } s.rubyforge_project = 'omniauth-auth0' s.files = `git ls-files`.split("\n") s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") s.executables = `git ls-files -- bin/*`.split('\n').map{ |f| File.basename(f) } s.require_paths = ['lib'] s.add_runtime_dependency 'omniauth-oauth2', '~> 1.4' s.add_development_dependency 'bundler', '~> 1.9' s.license = 'MIT' end omniauth-auth0-2.0.0/CHANGELOG.md0000644000175000017500000000516113256144763015227 0ustar pravipravi# Change Log ## [v2.0.0](https://github.com/auth0/omniauth-auth0/tree/v2.0.0) (2017-01-25) [Full Changelog](https://github.com/auth0/omniauth-auth0/compare/v1.4.1...v2.0.0) Updated library to handle OIDC conformant clients and OAuth2 features in Auth0. This affects how the `credentials` and `info` attributes are populated since the payload of /oauth/token and /userinfo are differnt when using OAuth2/OIDC features. The `credentials` hash will always have an `access_token` and might have a `refresh_token` (if it's allowed in your API settings in Auth0 dashboard and requested using `offline_access` scope) and an `id_token` (scope `openid` is needed for Auth0 to return it). The `info` object will use the [OmniAuth schema](https://github.com/omniauth/omniauth/wiki/Auth-Hash-Schema#schema-10-and-later) after calling /userinfo: - name: `name` attribute in userinfo response or `sub` if not available. - email: `email` attribute in userinfo response. - nickname: `nickname` attribute in userinfo response. - image: `picture` attribute in userinfo response. Also in `extra` will have in `raw_info` the full /userinfo response. ## [v1.4.1](https://github.com/auth0/omniauth-auth0/tree/v1.4.1) (2015-11-18) [Full Changelog](https://github.com/auth0/omniauth-auth0/compare/v1.4.0...v1.4.1) **Merged pull requests:** - Updating the strategy to set the refresh token in the credentials [\#14](https://github.com/auth0/omniauth-auth0/pull/14) ([LindseyB](https://github.com/LindseyB)) - Update README.md [\#13](https://github.com/auth0/omniauth-auth0/pull/13) ([Annyv2](https://github.com/Annyv2)) - Update home.js [\#12](https://github.com/auth0/omniauth-auth0/pull/12) ([Annyv2](https://github.com/Annyv2)) - Add nested module in version.rb [\#9](https://github.com/auth0/omniauth-auth0/pull/9) ([l4u](https://github.com/l4u)) ## [v1.4.0](https://github.com/auth0/omniauth-auth0/tree/v1.4.0) (2015-06-01) **Merged pull requests:** - Client headers [\#8](https://github.com/auth0/omniauth-auth0/pull/8) ([benschwarz](https://github.com/benschwarz)) - Web application seed with Lock [\#5](https://github.com/auth0/omniauth-auth0/pull/5) ([sandrinodimattia](https://github.com/sandrinodimattia)) - Create LICENSE.md [\#4](https://github.com/auth0/omniauth-auth0/pull/4) ([pose](https://github.com/pose)) - Update README.md [\#3](https://github.com/auth0/omniauth-auth0/pull/3) ([pose](https://github.com/pose)) - Fix Markdown typo [\#2](https://github.com/auth0/omniauth-auth0/pull/2) ([dentarg](https://github.com/dentarg)) \* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* omniauth-auth0-2.0.0/README.md0000644000175000017500000000763113256144763014701 0ustar pravipravi[![Build Status](https://travis-ci.org/auth0/omniauth-auth0.svg)](https://travis-ci.org/auth0/omniauth-auth0) # OmniAuth Auth0 This is the official [OmniAuth](https://github.com/intridea/omniauth) strategy for authenticating to [Auth0](https://auth0.com). ## Installing Add to your `Gemfile`: ```ruby gem 'omniauth-auth0' ``` Then `bundle install`. ## Usage ### Rails ```ruby Rails.application.config.middleware.use OmniAuth::Builder do provider :auth0, ENV['AUTH0_CLIENT_ID'], ENV['AUTH0_CLIENT_SECRET'], ENV['AUTH0_DOMAIN'] end ``` Then to redirect to your tenant's hosted login page: ```ruby redirect_to '/auth/auth0' ``` ### Sinatra ```ruby use OmniAuth::Builder do provider :auth0, ENV['AUTH0_CLIENT_ID'], ENV['AUTH0_CLIENT_SECRET'], ENV['AUTH0_DOMAIN'] end ``` Then to redirect to your tenant's hosted login page: ```ruby redirect to('/auth/auth0') ``` > You can customize your hosted login page in your [Auth0 Dashboard](https://manage.auth0.com/#/login_page) ### Auth parameters To send additional parameters during login you can specify them when you register the provider ```ruby provider :auth0, ENV['AUTH0_CLIENT_ID'], ENV['AUTH0_CLIENT_SECRET'], ENV['AUTH0_DOMAIN'], { authorize_params: { scope: 'openid read:users write:order', audience: 'https://mydomain/api' } } ``` that will tell it to send those parameters on every Auth request. Or you can do it for a specific Auth request by adding them in the query parameter of the redirect url: ```ruby redirect_to '/auth/auth0?connection=google-oauth2' ``` ### Auth Hash Auth0 strategy will have the standard OmniAuth hash attributes: - provider: the name of the strategy, in this case `auth0` - uid: the user identifier - info: the result of the call to /userinfo using OmniAuth standard attributes - credentials: Auth0 tokens, at least will have an access_token but can eventually have refresh_token and/or id_token - extra: Additional info obtained from calling /userinfo in the attribute `raw_info` ```ruby { :provider => 'auth0', :uid => 'google-oauth2|this-is-the-google-id', :info => { :name => 'John Foo', :email => 'johnfoo@example.org', :nickname => 'john', :image => 'https://example.org/john.jpg' }, :credentials => { :token => 'XdDadllcas2134rdfdsI', :expires_at => 1485373937, :expires => true, :refresh_token => 'aKNajdjfj123nBasd', :id_token => 'eyJhbGciOiJIUzI1NiIsImN0eSI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBGb28ifQ.lxAiy1rqve8ZHQEQVehUlP1sommPHVJDhgPgFPnDosg', :token_type => 'bearer', }, :extra => { :raw_info => { :email => 'johnfoo@example.org', :email_verified => 'true', :name => 'John Foo', :picture => 'https://example.org/john.jpg', :user_id => 'google-oauth2|this-is-the-google-id', :nickname => 'john', :created_at: '2014-07-15T17:19:50.387Z' } } } ``` ### ActionDispatch::Cookies::CookieOverflow issue If you are getting this error it means that you are using Cookie sessions and since you are storing the whole profile it overflows the max-size of 4K. You can change to use In-Memory store for development as follows: # /config/initializers/session_store.rb CrazyApp::Application.config.session_store :cache_store # /config/environments/development.rb config.cache_store = :memory_store ## Documentation For more information about [auth0](http://auth0.com) contact our [documentation page](http://docs.auth0.com/). ## Issue Reporting If you have found a bug or if you have a feature request, please report them at this repository issues section. Please do not report security vulnerabilities on the public GitHub issue tracker. The [Responsible Disclosure Program](https://auth0.com/whitehat) details the procedure for disclosing security issues. ## Author [Auth0](https://auth0.com) ## License This project is licensed under the MIT license. See the [LICENSE](LICENSE) file for more info. omniauth-auth0-2.0.0/Guardfile0000644000175000017500000000027013256144763015237 0ustar pravipraviguard :rspec, cmd: 'bundle exec rspec' do watch(%r{^spec/.+_spec\.rb$}) watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" } watch('spec/spec_helper.rb') { 'spec' } end omniauth-auth0-2.0.0/.rubocop.yml0000644000175000017500000000056013256144763015666 0ustar pravipraviMetrics/BlockLength: Exclude: - 'Rakefile' - '**/*.rake' - 'spec/**/*.rb' - 'spec/spec_helper.rb' Metrics/MethodLength: Exclude: - 'Rakefile' - '**/*.rake' - 'spec/**/*.rb' Metrics/AbcSize: Exclude: - 'Rakefile' - '**/*.rake' - 'spec/**/*.rb' - 'spec/spec_helper.rb' AllCops: Exclude: - 'omniauth-auth0.gemspec' omniauth-auth0-2.0.0/lib/0000755000175000017500000000000013256144763014161 5ustar pravipraviomniauth-auth0-2.0.0/lib/omniauth/0000755000175000017500000000000013256144763016005 5ustar pravipraviomniauth-auth0-2.0.0/lib/omniauth/strategies/0000755000175000017500000000000013256144763020157 5ustar pravipraviomniauth-auth0-2.0.0/lib/omniauth/strategies/auth0.rb0000644000175000017500000000455013256144763021531 0ustar pravipravirequire 'base64' require 'uri' require 'omniauth-oauth2' module OmniAuth module Strategies # Auth0 OmniAuth strategy class Auth0 < OmniAuth::Strategies::OAuth2 option :name, 'auth0' args [ :client_id, :client_secret, :domain ] def client options.client_options.site = domain_url options.client_options.authorize_url = '/authorize' options.client_options.token_url = '/oauth/token' options.client_options.userinfo_url = '/userinfo' super end uid { raw_info['sub'] } credentials do hash = { 'token' => access_token.token } hash['expires'] = true if access_token.params hash['id_token'] = access_token.params['id_token'] hash['token_type'] = access_token.params['token_type'] hash['refresh_token'] = access_token.refresh_token end hash end extra do { raw_info: raw_info } end info do { name: raw_info['name'] || raw_info['sub'], nickname: raw_info['nickname'], email: raw_info['email'], image: raw_info['picture'] } end def authorize_params params = super params['auth0Client'] = client_info params end def request_phase if no_client_id? fail!(:missing_client_id) elsif no_client_secret? fail!(:missing_client_secret) elsif no_domain? fail!(:missing_domain) else super end end private def raw_info userinfo_url = options.client_options.userinfo_url @raw_info ||= access_token.get(userinfo_url).parsed end def no_client_id? ['', nil].include?(options.client_id) end def no_client_secret? ['', nil].include?(options.client_secret) end def no_domain? ['', nil].include?(options.domain) end def domain_url domain_url = URI(options.domain) domain_url = URI("https://#{domain_url}") if domain_url.scheme.nil? domain_url.to_s end def client_info client_info = JSON.dump( name: 'omniauth-auth0', version: OmniAuth::Auth0::VERSION ) Base64.urlsafe_encode64(client_info) end end end end omniauth-auth0-2.0.0/lib/omniauth-auth0/0000755000175000017500000000000013256144763017024 5ustar pravipraviomniauth-auth0-2.0.0/lib/omniauth-auth0/version.rb0000644000175000017500000000010613256144763021033 0ustar pravipravimodule OmniAuth module Auth0 VERSION = '2.0.0'.freeze end end omniauth-auth0-2.0.0/lib/omniauth-auth0.rb0000644000175000017500000000014613256144763017352 0ustar pravipravirequire 'omniauth-auth0/version' # rubocop:disable Style/FileName require 'omniauth/strategies/auth0' omniauth-auth0-2.0.0/.travis.yml0000644000175000017500000000007313256144763015524 0ustar pravipravilanguage: ruby rvm: - 2.2.5 branches: only: - master omniauth-auth0-2.0.0/.gitignore0000644000175000017500000000007513256144763015405 0ustar pravipravi.ruby-version coverage Gemfile.lock *.gem .#* .env log/ tmp/omniauth-auth0-2.0.0/LICENSE0000644000175000017500000000213513256144763014421 0ustar pravipraviThe MIT License (MIT) Copyright (c) 2015 Auth0, Inc. (http://auth0.com) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. omniauth-auth0-2.0.0/Rakefile0000644000175000017500000000105013256144763015054 0ustar pravipravi#!/usr/bin/env rake require 'bundler/gem_tasks' require 'rspec/core/rake_task' desc 'Run specs' RSpec::Core::RakeTask.new begin require 'rubocop/rake_task' RuboCop::RakeTask.new rescue LoadError task :rubocop do $stderr.puts 'Rubocop is disabled' end end namespace :sinatra do task :start do system 'bundle exec shotgun' \ ' --server=thin --port=3000' \ ' examples/sinatra/config.ru' end end desc 'Run specs' task default: [:spec, :rubocop] task test: :spec task :guard do system 'bundle exec guard' end omniauth-auth0-2.0.0/Gemfile0000644000175000017500000000063613256144763014713 0ustar pravipravisource 'http://rubygems.org' gemspec gem 'gem-release' gem 'rake' group :development do gem 'dotenv' gem 'pry' gem 'shotgun' gem 'sinatra' gem 'thin' end group :test do gem 'guard-rspec', require: false gem 'listen', '~> 3.1.5' gem 'rack-test' gem 'rspec', '~> 3.5' gem 'rubocop', '>= 0.30', platforms: [ :ruby_19, :ruby_20, :ruby_21, :ruby_22 ] gem 'simplecov' gem 'webmock' end omniauth-auth0-2.0.0/.rspec0000644000175000017500000000005713256144763014532 0ustar pravipravi--color --require spec_helper --format=progressomniauth-auth0-2.0.0/spec/0000755000175000017500000000000013256144763014345 5ustar pravipraviomniauth-auth0-2.0.0/spec/omniauth/0000755000175000017500000000000013256144763016171 5ustar pravipraviomniauth-auth0-2.0.0/spec/omniauth/strategies/0000755000175000017500000000000013256144763020343 5ustar pravipraviomniauth-auth0-2.0.0/spec/omniauth/strategies/auth0_spec.rb0000644000175000017500000002001713256144763022723 0ustar pravipravirequire 'spec_helper' RSpec.shared_examples 'site has valid domain url' do |url| it { expect(subject.site).to eq(url) } end describe OmniAuth::Strategies::Auth0 do let(:client_id) { 'CLIENT_ID' } let(:client_secret) { 'CLIENT_SECRET' } let(:domain_url) { 'https://samples.auth0.com' } let(:application) do lambda do [200, {}, ['Hello.']] end end let(:auth0) do OmniAuth::Strategies::Auth0.new( application, client_id, client_secret, domain_url ) end describe 'client_options' do let(:subject) { auth0.client } context 'domain with https' do let(:domain_url) { 'https://samples.auth0.com' } it_behaves_like 'site has valid domain url', 'https://samples.auth0.com' end context 'domain with http' do let(:domain_url) { 'http://mydomain.com' } it_behaves_like 'site has valid domain url', 'http://mydomain.com' end context 'domain with host only' do let(:domain_url) { 'samples.auth0.com' } it_behaves_like 'site has valid domain url', 'https://samples.auth0.com' end it 'should have correct authorize path' do expect(subject.options[:authorize_url]).to eq('/authorize') end it 'should have the correct userinfo path' do expect(subject.options[:userinfo_url]).to eq('/userinfo') end it 'should have the correct token path' do expect(subject.options[:token_url]).to eq('/oauth/token') end end describe 'options' do let(:subject) { auth0.options } it 'should have the correct client_id' do expect(subject[:client_id]).to eq(client_id) end it 'should have the correct client secret' do expect(subject[:client_secret]).to eq(client_secret) end it 'should have correct domain' do expect(subject[:domain]).to eq(domain_url) end end describe 'oauth' do it 'redirects to hosted login page' do get 'auth/auth0' expect(last_response.status).to eq(302) redirect_url = last_response.headers['Location'] expect(redirect_url).to start_with('https://samples.auth0.com/authorize') expect(redirect_url).to have_query('response_type', 'code') expect(redirect_url).to have_query('state') expect(redirect_url).to have_query('client_id') expect(redirect_url).to have_query('redirect_uri') end describe 'callback' do let(:access_token) { 'access token' } let(:expires_in) { 2000 } let(:token_type) { 'bearer' } let(:refresh_token) { 'refresh token' } let(:id_token) { 'id token' } let(:user_id) { 'user identifier' } let(:state) { SecureRandom.hex(8) } let(:name) { 'John' } let(:nickname) { 'J' } let(:picture) { 'some picture url' } let(:email) { 'mail@mail.com' } let(:email_verified) { true } let(:oauth_response) do { access_token: access_token, expires_in: expires_in, token_type: token_type } end let(:oidc_response) do { id_token: id_token, access_token: access_token, expires_in: expires_in, token_type: token_type } end let(:basic_user_info) { { sub: user_id } } let(:oidc_user_info) do { sub: user_id, name: name, nickname: nickname, email: email, picture: picture, email_verified: email_verified } end def stub_auth(body) stub_request(:post, 'https://samples.auth0.com/oauth/token') .to_return( headers: { 'Content-Type' => 'application/json' }, body: MultiJson.encode(body) ) end def stub_userinfo(body) stub_request(:get, 'https://samples.auth0.com/userinfo') .to_return( headers: { 'Content-Type' => 'application/json' }, body: MultiJson.encode(body) ) end def trigger_callback get '/auth/auth0/callback', { 'state' => state }, 'rack.session' => { 'omniauth.state' => state } end before(:each) do WebMock.reset! end let(:subject) { MultiJson.decode(last_response.body) } context 'basic oauth' do before do stub_auth(oauth_response) stub_userinfo(basic_user_info) trigger_callback end it 'to succeed' do expect(last_response.status).to eq(200) end it 'has credentials' do expect(subject['credentials']['token']).to eq(access_token) expect(subject['credentials']['expires']).to be true expect(subject['credentials']['expires_at']).to_not be_nil end it 'has basic values' do expect(subject['provider']).to eq('auth0') expect(subject['uid']).to eq(user_id) expect(subject['info']['name']).to eq(user_id) end end context 'basic oauth w/refresh token' do before do stub_auth(oauth_response.merge(refresh_token: refresh_token)) stub_userinfo(basic_user_info) trigger_callback end it 'to succeed' do expect(last_response.status).to eq(200) end it 'has credentials' do expect(subject['credentials']['token']).to eq(access_token) expect(subject['credentials']['refresh_token']).to eq(refresh_token) expect(subject['credentials']['expires']).to be true expect(subject['credentials']['expires_at']).to_not be_nil end end context 'oidc' do before do stub_auth(oidc_response) stub_userinfo(oidc_user_info) trigger_callback end it 'to succeed' do expect(last_response.status).to eq(200) end it 'has credentials' do expect(subject['credentials']['token']).to eq(access_token) expect(subject['credentials']['expires']).to be true expect(subject['credentials']['expires_at']).to_not be_nil expect(subject['credentials']['id_token']).to eq(id_token) end it 'has basic values' do expect(subject['provider']).to eq('auth0') expect(subject['uid']).to eq(user_id) end it 'has info' do expect(subject['info']['name']).to eq(name) expect(subject['info']['nickname']).to eq(nickname) expect(subject['info']['image']).to eq(picture) expect(subject['info']['email']).to eq(email) end it 'has extra' do expect(subject['extra']['raw_info']['email_verified']).to be true end end end end describe 'error_handling' do it 'fails when missing client_id' do @app = make_application(client_id: nil) get 'auth/auth0' expect(last_response.status).to eq(302) redirect_url = last_response.headers['Location'] expect(redirect_url).to fail_auth_with('missing_client_id') end it 'fails when missing client_secret' do @app = make_application(client_secret: nil) get 'auth/auth0' expect(last_response.status).to eq(302) redirect_url = last_response.headers['Location'] expect(redirect_url).to fail_auth_with('missing_client_secret') end it 'fails when missing domain' do @app = make_application(domain: nil) get 'auth/auth0' expect(last_response.status).to eq(302) redirect_url = last_response.headers['Location'] expect(redirect_url).to fail_auth_with('missing_domain') end end end RSpec::Matchers.define :fail_auth_with do |message| match do |actual| uri = URI(actual) query = CGI.parse(uri.query) (uri.path == '/auth/failure') && (query['message'] == [message]) && (query['strategy'] == ['auth0']) end end RSpec::Matchers.define :have_query do |key, value| match do |actual| uri = redirect_uri(actual) query = query(uri) if value.nil? query[key].length == 1 else query[key] == [value] end end def redirect_uri(string) URI(string) end def query(uri) CGI.parse(uri.query) end end omniauth-auth0-2.0.0/spec/spec_helper.rb0000644000175000017500000000244213256144763017165 0ustar pravipravi$LOAD_PATH.unshift File.expand_path('..', __FILE__) $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) require 'simplecov' if ENV['COVERAGE'] SimpleCov.start do minimum_coverage(89.8) end end require 'rspec' require 'rack/test' require 'webmock/rspec' require 'omniauth' require 'omniauth-auth0' require 'sinatra' WebMock.disable_net_connect! RSpec.configure do |config| config.include WebMock::API config.include Rack::Test::Methods config.extend OmniAuth::Test::StrategyMacros, type: :strategy def app @app || make_application end def make_application(options = {}) client_id = 'CLIENT_ID' secret = 'CLIENT_SECRET' domain = 'samples.auth0.com' client_id = options.delete(:client_id) if options.key?(:client_id) secret = options.delete(:client_secret) if options.key?(:client_secret) domain = options.delete(:domain) if options.key?(:domain) Sinatra.new do configure do enable :sessions set :show_exceptions, false set :session_secret, 'TEST' end use OmniAuth::Builder do provider :auth0, client_id, secret, domain, options end get '/auth/auth0/callback' do MultiJson.encode(env['omniauth.auth']) end end end end OmniAuth.config.logger = Logger.new('/dev/null') omniauth-auth0-2.0.0/.gemrelease0000644000175000017500000000002113256144763015516 0ustar pravipravibump: tag: true