osso 0.0.3.3 → 0.0.3.4

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 +4 -4
  2. data/.gitignore +1 -1
  3. data/Gemfile.lock +3 -1
  4. data/db/schema.rb +1 -0
  5. data/lib/osso.rb +1 -0
  6. data/lib/osso/graphql/mutation.rb +15 -0
  7. data/lib/osso/graphql/mutations.rb +10 -0
  8. data/lib/osso/graphql/mutations/base_mutation.rb +24 -0
  9. data/lib/osso/graphql/mutations/configure_identity_provider.rb +30 -0
  10. data/lib/osso/graphql/mutations/create_identity_provider.rb +28 -0
  11. data/lib/osso/graphql/mutations/set_saml_provider.rb +27 -0
  12. data/lib/osso/graphql/query.rb +25 -0
  13. data/lib/osso/graphql/resolvers.rb +12 -0
  14. data/lib/osso/graphql/resolvers/enterprise_account.rb +25 -0
  15. data/lib/osso/graphql/resolvers/enterprise_accounts.rb +17 -0
  16. data/lib/osso/graphql/resolvers/oauth_clients.rb +15 -0
  17. data/lib/osso/graphql/schema.rb +42 -0
  18. data/lib/osso/graphql/types.rb +14 -0
  19. data/lib/osso/graphql/types/base_enum.rb +10 -0
  20. data/lib/osso/graphql/types/base_object.rb +12 -0
  21. data/lib/osso/graphql/types/enterprise_account.rb +33 -0
  22. data/lib/osso/graphql/types/identity_provider.rb +40 -0
  23. data/lib/osso/graphql/types/identity_provider_service.rb +12 -0
  24. data/lib/osso/graphql/types/oauth_client.rb +20 -0
  25. data/lib/osso/graphql/types/user.rb +17 -0
  26. data/lib/osso/helpers/auth.rb +51 -49
  27. data/lib/osso/helpers/helpers.rb +3 -1
  28. data/lib/osso/routes/admin.rb +1 -5
  29. data/lib/osso/routes/oauth.rb +2 -1
  30. data/lib/osso/version.rb +1 -1
  31. data/osso-rb.gemspec +1 -0
  32. data/spec/routes/admin_spec.rb +1 -0
  33. data/spec/spec_helper.rb +4 -0
  34. data/spec/support/views/admin.erb +5 -0
  35. metadata +38 -2
  36. data/spec/support/vcr_cassettes/okta_saml_callback.yml +0 -59
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6b1788a4933cd6a5e4fef31d6e92c8c63cc9fd9e6cf6fb838b71f65161e9845b
4
- data.tar.gz: 82508ceb2a7af276c64a6a1ae2ba3febc118e71d4c89fcb194db1d0ec35c686a
3
+ metadata.gz: 7da5a91a5eda62f96a11772d72afc0e1477e586218233466d2dcd37a89e2aabf
4
+ data.tar.gz: 839e333e245142b6fc29738242293f205bae28a4e768e9f35b87d0572708f9f4
5
5
  SHA512:
6
- metadata.gz: 363d13744052824deea9a26478549daac1dfd49d850577329f6c87efbe4f0de23d1aca40ee1aa39b8cd4324503267e5e81b192086532db796c122ca79e2a9c14
7
- data.tar.gz: 9313221346428718b1d06b2ef10c9dcb453cd02716f4ccfaa956e956d19b420290c0818215cebba204aa843acb5a668dfdb6d11e285de1f3f847a786968c78a0
6
+ metadata.gz: 8ede35265fed16afeb20d28608c8b74053ed5e5f11e6a393df87395f5d0ec30f7a58a84834a99bd6c8a16cbe21eaee5f22a4fdec4c0bbf3a7e5d49a760b53f85
7
+ data.tar.gz: 3c37e0256ee1758ce915c25ce3d7b83e070ef2cadba85c41649c13b3ede1fa0371eeb10757ce990e47a8669edc3b65424749cf62b5fa8d46f21a132285302e08
data/.gitignore CHANGED
@@ -7,4 +7,4 @@
7
7
  /spec/reports/
8
8
  /tmp/
9
9
  *.gem
10
- *.DS_Store
10
+ .DS_Store
@@ -1,8 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- osso (0.0.3.3)
4
+ osso (0.0.3.4)
5
5
  activesupport (>= 6.0.3.2)
6
+ graphql
6
7
  jwt
7
8
  omniauth-multi-provider
8
9
  omniauth-saml
@@ -48,6 +49,7 @@ GEM
48
49
  activesupport (>= 5.0.0)
49
50
  faker (2.13.0)
50
51
  i18n (>= 1.6, < 2)
52
+ graphql (1.11.1)
51
53
  hashdiff (1.0.1)
52
54
  hashie (4.1.0)
53
55
  httpclient (2.8.3)
@@ -0,0 +1 @@
1
+ # frozen_string_literal: true
@@ -6,4 +6,5 @@ module Osso
6
6
  require_relative 'osso/lib/oauth2_token'
7
7
  require_relative 'osso/models/models'
8
8
  require_relative 'osso/routes/routes'
9
+ require_relative 'osso/graphql/schema'
9
10
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'mutations'
4
+
5
+ module Osso
6
+ module GraphQL
7
+ module Types
8
+ class MutationType < BaseObject
9
+ field :configure_identity_provider, mutation: Mutations::ConfigureIdentityProvider
10
+ field :create_identity_provider, mutation: Mutations::CreateIdentityProvider
11
+ field :set_saml_provider, mutation: Mutations::SetSamlProvider
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+ module Osso
3
+ module Mutations
4
+ end
5
+ end
6
+
7
+ require_relative 'mutations/base_mutation'
8
+ require_relative 'mutations/configure_identity_provider'
9
+ require_relative 'mutations/create_identity_provider'
10
+ require_relative 'mutations/set_saml_provider'
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Osso
4
+ module GraphQL
5
+ module Mutations
6
+ class BaseMutation < ::GraphQL::Schema::RelayClassicMutation
7
+ # This is used for generating payload types
8
+ object_class Types::BaseObject
9
+ # # This is used for return fields on the mutation's payload
10
+ # field_class Types::BaseField
11
+ # # This is used for generating the `input: { ... }` object type
12
+ # input_object_class Types::BaseInputObject
13
+
14
+ def return_data(data)
15
+ data.merge(errors: [])
16
+ end
17
+
18
+ def return_error(error)
19
+ error.merge(data: nil)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Osso
4
+ module GraphQL
5
+ module Mutations
6
+ class ConfigureIdentityProvider < BaseMutation
7
+ null false
8
+ argument :id, ID, required: true
9
+ argument :service, Types::IdentityProviderService, required: true
10
+ argument :sso_url, String, required: true
11
+ argument :sso_cert, String, required: true
12
+
13
+ field :identity_provider, Types::IdentityProvider, null: true
14
+ field :errors, [String], null: false
15
+
16
+ def resolve(id:, sso_url:, sso_cert:, service:)
17
+ provider = Osso::Models::SamlProvider.find(id)
18
+ provider.update(
19
+ idp_cert: sso_cert,
20
+ idp_sso_target_url: sso_url,
21
+ )
22
+
23
+ return_data(identity_provider: provider)
24
+ # rescue StandardError => e
25
+ # return_error(errors: e.full_message)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Osso
4
+ module GraphQL
5
+ module Mutations
6
+ class CreateIdentityProvider < BaseMutation
7
+ null false
8
+ argument :enterprise_account_id, ID, required: true
9
+ argument :provider_service, Types::IdentityProviderService, required: true
10
+
11
+ field :identity_provider, Types::IdentityProvider, null: false
12
+ field :errors, [String], null: false
13
+
14
+ def resolve(enterprise_account_id:, provider_service:)
15
+ enterprise_account = Osso::Models::EnterpriseAccount.find(enterprise_account_id)
16
+ identity_provider = enterprise_account.saml_providers.create!(
17
+ provider: provider_service || 'OKTA',
18
+ domain: enterprise_account.domain,
19
+ )
20
+
21
+ return_data(identity_provider: identity_provider)
22
+ rescue StandardError => e
23
+ return_error(errors: e.full_message)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Osso
4
+ module GraphQL
5
+ module Mutations
6
+ class SetSamlProvider < BaseMutation
7
+ null false
8
+
9
+ argument :provider, Types::IdentityProviderService, required: true
10
+ argument :id, ID, required: true
11
+
12
+ field :identity_provider, Types::IdentityProvider, null: false
13
+ field :errors, [String], null: false
14
+
15
+ def resolve(provider:, id:)
16
+ saml_provider = Osso::Models::SamlProvider.find(id)
17
+ saml_provider.provider = provider
18
+ saml_provider.save!
19
+ {
20
+ saml_provider: saml_provider,
21
+ errors: [],
22
+ }
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Osso
4
+ module GraphQL
5
+ module Types
6
+ class QueryType < ::GraphQL::Schema::Object
7
+ field :enterprise_accounts, null: true, resolver: Resolvers::EnterpriseAccounts
8
+ field :oauth_clients, null: true, resolver: Resolvers::OAuthClients
9
+
10
+ field :enterprise_account, null: false, resolver: Resolvers::EnterpriseAccount do
11
+ argument :domain, String, required: true
12
+ end
13
+
14
+ field(
15
+ :identity_provider,
16
+ Types::IdentityProvider,
17
+ null: true,
18
+ resolve: ->(_obj, args, _context) { Osso::Models::SamlProvider.find(args[:id]) },
19
+ ) do
20
+ argument :id, ID, required: true
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Osso
4
+ module GraphQL
5
+ module Resolvers
6
+ end
7
+ end
8
+ end
9
+
10
+ require_relative 'resolvers/enterprise_account'
11
+ require_relative 'resolvers/enterprise_accounts'
12
+ require_relative 'resolvers/oauth_clients'
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Osso
4
+ module GraphQL
5
+ module Resolvers
6
+ class EnterpriseAccount < ::GraphQL::Schema::Resolver
7
+ type Types::EnterpriseAccount, null: false
8
+
9
+ def resolve(args)
10
+ return unless admin? || enterprise_authorized?(args[:domain])
11
+
12
+ Osso::Models::EnterpriseAccount.find_by(domain: args[:domain])
13
+ end
14
+
15
+ def admin?
16
+ context[:scope] == :admin
17
+ end
18
+
19
+ def enterprise_authorized?(domain)
20
+ context[:scope] == domain
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Osso
4
+ module GraphQL
5
+ module Resolvers
6
+ class EnterpriseAccounts < ::GraphQL::Schema::Resolver
7
+ type [Types::EnterpriseAccount], null: true
8
+
9
+ def resolve
10
+ return Osso::Models::EnterpriseAccount.all if context[:scope] == :admin
11
+
12
+ Array(Osso::Models::EnterpriseAccount.find_by(domain: context[:scope]))
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Osso
4
+ module GraphQL
5
+ module Resolvers
6
+ class OAuthClients < ::GraphQL::Schema::Resolver
7
+ type [Types::OAuthClient], null: true
8
+
9
+ def resolve
10
+ return Osso::Models::OAuthClient.all if context[:scope] == :admin
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'graphql'
4
+ require_relative 'types'
5
+ require_relative 'resolvers'
6
+ require_relative 'mutation'
7
+ require_relative 'query'
8
+
9
+ GraphQL::Relay::BaseConnection.register_connection_implementation(
10
+ ActiveRecord::Relation,
11
+ GraphQL::Relay::RelationConnection,
12
+ )
13
+
14
+ module Osso
15
+ module GraphQL
16
+ class Schema < ::GraphQL::Schema
17
+ use ::GraphQL::Pagination::Connections
18
+ query Types::QueryType
19
+ mutation Types::MutationType
20
+
21
+ def self.id_from_object(object, _type_definition = nil, _query_ctx = nil)
22
+ GraphQL::Schema::UniqueWithinType.encode(object.class.name, object.id)
23
+ end
24
+
25
+ def self.object_from_id(id, _query_ctx = nil)
26
+ class_name, item_id = GraphQL::Schema::UniqueWithinType.decode(id)
27
+ Object.const_get(class_name).find(item_id)
28
+ end
29
+
30
+ def self.resolve_type(_type, obj, _ctx)
31
+ case obj
32
+ when Osso::Models::EnterpriseAccount
33
+ Types::EnterpriseAccount
34
+ when Osso::Models::SamlProvider
35
+ Types::IdentityProvider
36
+ else
37
+ raise("Unexpected object: #{obj}")
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Osso
4
+ module Types
5
+ end
6
+ end
7
+
8
+ require_relative 'types/base_object'
9
+ require_relative 'types/base_enum'
10
+ require_relative 'types/identity_provider_service'
11
+ require_relative 'types/identity_provider'
12
+ require_relative 'types/enterprise_account'
13
+ require_relative 'types/oauth_client'
14
+ require_relative 'types/user'
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Osso
4
+ module GraphQL
5
+ module Types
6
+ class BaseEnum < ::GraphQL::Schema::Enum
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'graphql'
4
+
5
+ module Osso
6
+ module GraphQL
7
+ module Types
8
+ class BaseObject < ::GraphQL::Schema::Object
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'graphql'
4
+
5
+ module Osso
6
+ module GraphQL
7
+ module Types
8
+ class EnterpriseAccount < Types::BaseObject
9
+ description 'An Account for a company that wishes to use SAML via Osso'
10
+ implements ::GraphQL::Types::Relay::Node
11
+
12
+ global_id_field :gid
13
+ field :id, ID, null: false
14
+ field :name, String, null: false
15
+ field :domain, String, null: false
16
+ field :identity_providers, [Types::IdentityProvider], null: true
17
+ field :status, String, null: false
18
+
19
+ def name
20
+ object.domain.gsub('.com', '')
21
+ end
22
+
23
+ def status
24
+ 'active'
25
+ end
26
+
27
+ def identity_providers
28
+ object.saml_providers
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'graphql'
4
+
5
+ module Osso
6
+ module GraphQL
7
+ module Types
8
+ class IdentityProvider < Types::BaseObject
9
+ description 'Represents a SAML based IDP instance for an EnterpriseAccount'
10
+ implements ::GraphQL::Types::Relay::Node
11
+
12
+ global_id_field :gid
13
+ field :id, ID, null: false
14
+ field :enterprise_account_id, ID, null: false
15
+ field :service, Types::IdentityProviderService, null: true
16
+ field :domain, String, null: false
17
+ field :acs_url, String, null: false
18
+ field :sso_url, String, null: true
19
+ field :sso_cert, String, null: true
20
+ field :configured, Boolean, null: false
21
+
22
+ def service
23
+ @object.provider
24
+ end
25
+
26
+ def configured
27
+ @object.idp_sso_target_url && @object.idp_cert
28
+ end
29
+
30
+ def sso_cert
31
+ @object.idp_cert
32
+ end
33
+
34
+ def sso_url
35
+ @object.idp_sso_target_url
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Osso
4
+ module GraphQL
5
+ module Types
6
+ class IdentityProviderService < BaseEnum
7
+ value('AZURE', 'Microsoft Azure Identity Provider', value: 'Osso::Models::AzureSamlProvider')
8
+ value('OKTA', 'Okta Identity Provider', value: 'Osso::Models::OktaSamlProvider')
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'graphql'
4
+
5
+ module Osso
6
+ module GraphQL
7
+ module Types
8
+ class OAuthClient < Types::BaseObject
9
+ description 'An OAuth client used to consume Osso SAML users'
10
+ implements ::GraphQL::Types::Relay::Node
11
+
12
+ global_id_field :gid
13
+ field :id, ID, null: false
14
+ field :name, String, null: false
15
+ field :client_id, String, null: false
16
+ field :client_secret, String, null: false
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'graphql'
4
+ require_relative 'base_object'
5
+
6
+ module Osso
7
+ module GraphQL
8
+ module Types
9
+ class User < Types::BaseObject
10
+ description 'A User of the application'
11
+
12
+ field :id, ID, null: false
13
+ field :name, String, null: true
14
+ end
15
+ end
16
+ end
17
+ end
@@ -1,67 +1,69 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Helpers
4
- module Auth
5
- attr_accessor :current_scope
6
-
7
- def enterprise_protected!(domain = nil)
8
- return if admin_authorized?
9
- return if enterprise_authorized?(domain)
3
+ module Osso
4
+ module Helpers
5
+ module Auth
6
+ attr_accessor :current_scope
7
+
8
+ def enterprise_protected!(domain = nil)
9
+ return if admin_authorized?
10
+ return if enterprise_authorized?(domain)
10
11
 
11
- redirect ENV['JWT_URL']
12
- end
12
+ redirect ENV['JWT_URL']
13
+ end
13
14
 
14
- def enterprise_authorized?(domain)
15
- payload, _args = JWT.decode(
16
- token,
17
- ENV['JWT_HMAC_SECRET'],
18
- true,
19
- { algorithm: 'HS256' },
20
- )
15
+ def enterprise_authorized?(domain)
16
+ payload, _args = JWT.decode(
17
+ token,
18
+ ENV['JWT_HMAC_SECRET'],
19
+ true,
20
+ { algorithm: 'HS256' },
21
+ )
21
22
 
22
- @current_scope = payload['scope']
23
+ @current_scope = payload['scope']
23
24
 
24
- true
25
- rescue JWT::DecodeError
26
- false
27
- end
25
+ true
26
+ rescue JWT::DecodeError
27
+ false
28
+ end
28
29
 
29
- def admin_protected!
30
- return if admin_authorized?
30
+ def admin_protected!
31
+ return if admin_authorized?
31
32
 
32
- redirect ENV['JWT_URL']
33
- end
33
+ redirect ENV['JWT_URL']
34
+ end
34
35
 
35
- def admin_authorized?
36
- payload, _args = JWT.decode(
37
- token,
38
- ENV['JWT_HMAC_SECRET'],
39
- true,
40
- { algorithm: 'HS256' },
41
- )
36
+ def admin_authorized?
37
+ payload, _args = JWT.decode(
38
+ token,
39
+ ENV['JWT_HMAC_SECRET'],
40
+ true,
41
+ { algorithm: 'HS256' },
42
+ )
42
43
 
43
- if payload['scope'] == 'admin'
44
- @current_scope = :admin
45
- return true
46
- end
44
+ if payload['scope'] == 'admin'
45
+ @current_scope = :admin
46
+ return true
47
+ end
47
48
 
48
- false
49
- rescue JWT::DecodeError
50
- false
51
- end
49
+ false
50
+ rescue JWT::DecodeError
51
+ false
52
+ end
52
53
 
53
- def token
54
- request.env['admin_token'] || session['admin_token'] || request['admin_token']
55
- end
54
+ def token
55
+ request.env['admin_token'] || session['admin_token'] || request['admin_token']
56
+ end
56
57
 
57
- def chomp_token
58
- return unless request['admin_token'].present?
58
+ def chomp_token
59
+ return unless request['admin_token'].present?
59
60
 
60
- session['admin_token'] = request['admin_token']
61
+ session['admin_token'] = request['admin_token']
61
62
 
62
- return if request.post?
63
+ return if request.post?
63
64
 
64
- redirect request.path
65
+ redirect request.path
66
+ end
65
67
  end
66
68
  end
67
- end
69
+ end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Helpers
3
+ module Osso
4
+ module Helpers
5
+ end
4
6
  end
5
7
 
6
8
  require_relative 'auth'
@@ -14,7 +14,7 @@ module Osso
14
14
  get '/' do
15
15
  admin_protected!
16
16
 
17
- erb :'public/index'
17
+ erb :admin
18
18
  end
19
19
 
20
20
  get '/enterprise' do
@@ -26,10 +26,6 @@ module Osso
26
26
  get '/enterprise/:domain' do
27
27
  enterprise_protected!(params[:domain])
28
28
 
29
- @enterprise = Models::EnterpriseAccount.where(
30
- domain: params[:domain],
31
- ).first_or_create
32
-
33
29
  erb :admin
34
30
  end
35
31
 
@@ -24,7 +24,8 @@ module Osso
24
24
  redirect "/auth/saml/#{@enterprise.provider.id}"
25
25
  end
26
26
 
27
- erb :multiple_providers
27
+ # TODO: multiple provider support
28
+ # erb :multiple_providers
28
29
 
29
30
  rescue Rack::OAuth2::Server::Authorize::BadRequest => e
30
31
  @error = e
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Osso
4
- VERSION = '0.0.3.3'
4
+ VERSION = '0.0.3.4'
5
5
  end
@@ -15,6 +15,7 @@ Gem::Specification.new do |spec|
15
15
  spec.license = 'MIT'
16
16
 
17
17
  spec.add_runtime_dependency 'activesupport', '>= 6.0.3.2'
18
+ spec.add_runtime_dependency 'graphql'
18
19
  spec.add_runtime_dependency 'jwt'
19
20
  spec.add_runtime_dependency 'omniauth-multi-provider'
20
21
  spec.add_runtime_dependency 'omniauth-saml'
@@ -9,6 +9,7 @@ describe Osso::Admin do
9
9
  before do
10
10
  ENV['JWT_URL'] = jwt_url
11
11
  ENV['JWT_HMAC_SECRET'] = jwt_hmac_secret
12
+ described_class.set(:views, spec_views)
12
13
  end
13
14
 
14
15
  describe 'get /admin' do
@@ -40,6 +40,10 @@ module RSpecMixin
40
40
  def last_json_response
41
41
  JSON.parse(last_response.body, symbolize_names: true)
42
42
  end
43
+
44
+ def spec_views
45
+ File.dirname(__FILE__) + '/support/views'
46
+ end
43
47
  end
44
48
 
45
49
  RSpec.configure do |config|
@@ -0,0 +1,5 @@
1
+ <%#
2
+ NB: this file exists so that the admin routes have something to render in spec.
3
+ In real-world usage, those routes render an index.html file that includes the
4
+ React app.
5
+ %>
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: osso
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3.3
4
+ version: 0.0.3.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Bauch
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: 6.0.3.2
27
+ - !ruby/object:Gem::Dependency
28
+ name: graphql
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: jwt
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -218,6 +232,7 @@ files:
218
232
  - bin/console
219
233
  - bin/setup
220
234
  - config/database.yml
235
+ - db/schema.rb
221
236
  - lib/.DS_Store
222
237
  - lib/osso.rb
223
238
  - lib/osso/Rakefile
@@ -237,6 +252,27 @@ files:
237
252
  - lib/osso/db/migrate/20200502135008_add_oauth_client_id_to_enterprise_account.rb
238
253
  - lib/osso/db/migrate/20200601131227_drop_null_constraint_from_saml_providers_provider.rb
239
254
  - lib/osso/db/schema.rb
255
+ - lib/osso/graphql/.DS_Store
256
+ - lib/osso/graphql/mutation.rb
257
+ - lib/osso/graphql/mutations.rb
258
+ - lib/osso/graphql/mutations/base_mutation.rb
259
+ - lib/osso/graphql/mutations/configure_identity_provider.rb
260
+ - lib/osso/graphql/mutations/create_identity_provider.rb
261
+ - lib/osso/graphql/mutations/set_saml_provider.rb
262
+ - lib/osso/graphql/query.rb
263
+ - lib/osso/graphql/resolvers.rb
264
+ - lib/osso/graphql/resolvers/enterprise_account.rb
265
+ - lib/osso/graphql/resolvers/enterprise_accounts.rb
266
+ - lib/osso/graphql/resolvers/oauth_clients.rb
267
+ - lib/osso/graphql/schema.rb
268
+ - lib/osso/graphql/types.rb
269
+ - lib/osso/graphql/types/base_enum.rb
270
+ - lib/osso/graphql/types/base_object.rb
271
+ - lib/osso/graphql/types/enterprise_account.rb
272
+ - lib/osso/graphql/types/identity_provider.rb
273
+ - lib/osso/graphql/types/identity_provider_service.rb
274
+ - lib/osso/graphql/types/oauth_client.rb
275
+ - lib/osso/graphql/types/user.rb
240
276
  - lib/osso/helpers/auth.rb
241
277
  - lib/osso/helpers/helpers.rb
242
278
  - lib/osso/lib/app_config.rb
@@ -275,7 +311,7 @@ files:
275
311
  - spec/routes/auth_spec.rb
276
312
  - spec/routes/oauth_spec.rb
277
313
  - spec/spec_helper.rb
278
- - spec/support/vcr_cassettes/okta_saml_callback.yml
314
+ - spec/support/views/admin.erb
279
315
  homepage: https://github.com/enterprise-oss/osso-rb
280
316
  licenses:
281
317
  - MIT
@@ -1,59 +0,0 @@
1
- ---
2
- http_interactions:
3
- - request:
4
- method: post
5
- uri: http://localhost:9292/auth/saml/:uuid/callback
6
- body:
7
- encoding: ASCII-8BIT
8
- string: SAMLResponse=PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c2FtbDJwOlJlc3BvbnNlIERlc3RpbmF0aW9uPSJodHRwOi8vbG9jYWxob3N0OjkyOTIvYXV0aC9zYW1sLzY4ZTkwNTdjLTQ2MmYtNDM3Zi04NDRkLTk4NzI0ZmVmOWQ0My9jYWxsYmFjayIgSUQ9ImlkMTgyOTI5NzEwODgzMjU2OTIyMDE3Nzc0NDUyIiBJblJlc3BvbnNlVG89Il81NGM0ZjE5MS0xYmY3LTRiMDItOTZlYS0zNGQ4NTU1YWVjYWEiIElzc3VlSW5zdGFudD0iMjAyMC0wNC0xMVQxNjozMjoxOC40MjRaIiBWZXJzaW9uPSIyLjAiIHhtbG5zOnNhbWwycD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiPjxzYW1sMjpJc3N1ZXIgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6bmFtZWlkLWZvcm1hdDplbnRpdHkiIHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIj5odHRwOi8vd3d3Lm9rdGEuY29tL2V4azUxMzI2YjNVMTk0MUhmNHg2PC9zYW1sMjpJc3N1ZXI%2BPGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI%2BPGRzOlNpZ25lZEluZm8%2BPGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48ZHM6U2lnbmF0dXJlTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxkc2lnLW1vcmUjcnNhLXNoYTI1NiIvPjxkczpSZWZlcmVuY2UgVVJJPSIjaWQxODI5Mjk3MTA4ODMyNTY5MjIwMTc3NzQ0NTIiPjxkczpUcmFuc2Zvcm1zPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSIvPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiPjxlYzpJbmNsdXNpdmVOYW1lc3BhY2VzIFByZWZpeExpc3Q9InhzIiB4bWxuczplYz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8%2BPC9kczpUcmFuc2Zvcm0%2BPC9kczpUcmFuc2Zvcm1zPjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNzaGEyNTYiLz48ZHM6RGlnZXN0VmFsdWU%2BZEpWb0Z6Y01zTjV3TmNJRWtJWkxqb1JjWDRpMzVYekI3RGg4ZE0wU1pMZz08L2RzOkRpZ2VzdFZhbHVlPjwvZHM6UmVmZXJlbmNlPjwvZHM6U2lnbmVkSW5mbz48ZHM6U2lnbmF0dXJlVmFsdWU%2BdnFES0RhVk8vYlRkRW5hNlMwNHdQV0hicXdNQ1JHdStjRVRxOE9FR2pzK1drTUVBQVYzeEFnNitUZTB2Z0haSjlTWnhwSW9iWEdadVQvVFFnbnAyczNMM1FvaUx5UVFwOExXQnl0b1A0UWhSV1dNY1N3UHlUYWI5TmRUT1RWeXJBckJxZHFFVXN0M0Z5cVpLSVFlbUpocWU4ckk4cmJwQTI2YUFqc2xNZkRIZHNMTFlFMjUwTXpPdG9wdVppNHpvdDZDSTl1NFVRK2dsOWdqSUJyai9sZ3BQME5mZ3RXM01QNVRQOGxsaEpmVXJ4MjhLWGZjSHpOa1RDU1BxVTRwLzZCRTV5dm5QcmJVTE1EWWZQWG5aV0t5d0JpRkNyaTgydmNOdElXVHUvcmkrejNRT0p6dFRiTkdKU0ZjWHdkWHpheG8xdS9SN3ZUQlJPOCtuZEFsL1VBPT08L2RzOlNpZ25hdHVyZVZhbHVlPjxkczpLZXlJbmZvPjxkczpYNTA5RGF0YT48ZHM6WDUwOUNlcnRpZmljYXRlPk1JSURwRENDQW95Z0F3SUJBZ0lHQVhFaUQ0TGxNQTBHQ1NxR1NJYjNEUUVCQ3dVQU1JR1NNUXN3Q1FZRFZRUUdFd0pWVXpFVE1CRUcKQTFVRUNBd0tRMkZzYVdadmNtNXBZVEVXTUJRR0ExVUVCd3dOVTJGdUlFWnlZVzVqYVhOamJ6RU5NQXNHQTFVRUNnd0VUMnQwWVRFVQpNQklHQTFVRUN3d0xVMU5QVUhKdmRtbGtaWEl4RXpBUkJnTlZCQU1NQ21SbGRpMHhOakl3TWpReEhEQWFCZ2txaGtpRzl3MEJDUUVXCkRXbHVabTlBYjJ0MFlTNWpiMjB3SGhjTk1qQXdNekk0TVRZMU1UVTBXaGNOTXpBd016STRNVFkxTWpVMFdqQ0JrakVMTUFrR0ExVUUKQmhNQ1ZWTXhFekFSQmdOVkJBZ01Da05oYkdsbWIzSnVhV0V4RmpBVUJnTlZCQWNNRFZOaGJpQkdjbUZ1WTJselkyOHhEVEFMQmdOVgpCQW9NQkU5cmRHRXhGREFTQmdOVkJBc01DMU5UVDFCeWIzWnBaR1Z5TVJNd0VRWURWUVFEREFwa1pYWXRNVFl5TURJME1Sd3dHZ1lKCktvWklodmNOQVFrQkZnMXBibVp2UUc5cmRHRXVZMjl0TUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUEKd3NuUDRVVGZ2M2J4UjVKaDBhdDUxRHFqaitmS3hGem56RlczWEE1TmJGMlNsUkxqZVljdmozKzQ3VEMwZVA2eE9zTFdmbnZkbng0dgpkZDlVZm43akRDbzVwTDNKeWtNVkVoMkkwc3pGM1JMQythNTMyQXJjd2dVOVB4NDgrcldWd1BrQVNTN2w0TkhBTTQrZ09CSEpNUXQyCkFNb2hQVDBrVTQxUDhCRVB6ZndoTnlpRVhSNjZKTlpJSlVFOGZNM1ZwZ254bS9WU3dZekpmME5mT3lmeHY4SmN6RjB6a0RicEU3VGsKM1d3L1BGRkxvTXhXemFuV0dKUStibG5odjZVVjZINGZjZkFiY3dBcGxPZElWSGpTMmdoWUJ2WU5HYWh1RnhqaWEwKzZjc3laR3J0OApINFhtUjVEcitqWFk1SzFiMVZPQTBrMTkvRkNuSEhOL3NtbjI1d0lEQVFBQk1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQmdEOU5FCjRPQ3VSMSt2dWNWOFMxVDZYWElMMmhCN2JYQkFaRVZIWjFhRXJSemt0Z1hBTWdWd0cyNjd2SWtENVZPWEJpVHk5eU5VNUxLNkczazIKemV3VTE5MHNMMWRNZnlQbm9WWnluOTRudndlOUErb24wdG1aZG1rMDB4aXJLazNGSmRhY25aTkU5RGwvYWZJcmNOZjZ4QW0wV3NVOQprYk1pUnd3dmpPNFRBaXlnRFF6YnJSQzhaZm1UM2hwQmEzYVRVekFjY3J2RVFjZ2FyTGs0cjdValhQN2EybUNOM1VJSWgrc25OMk1zCnZYSEwwcjZmTTN4Ym5peis1bGxlV3RQRnc3M3l5U0JjOHpua1daNFRuOExoMHI2bzVuQ1JZYnIyUkVVQjdaSWZpSXlCYlp4SXA0a3YKYStoYWJiblFERmlOVnpFZDhPUFhIaDRFcUx4T1BEUlc8L2RzOlg1MDlDZXJ0aWZpY2F0ZT48L2RzOlg1MDlEYXRhPjwvZHM6S2V5SW5mbz48L2RzOlNpZ25hdHVyZT48c2FtbDJwOlN0YXR1cyB4bWxuczpzYW1sMnA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCI%2BPHNhbWwycDpTdGF0dXNDb2RlIFZhbHVlPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6c3RhdHVzOlN1Y2Nlc3MiLz48L3NhbWwycDpTdGF0dXM%2BPHNhbWwyOkFzc2VydGlvbiBJRD0iaWQxODI5Mjk3MTA4ODQyMDg3MzY4MzU5Njg1MiIgSXNzdWVJbnN0YW50PSIyMDIwLTA0LTExVDE2OjMyOjE4LjQyNFoiIFZlcnNpb249IjIuMCIgeG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSI%2BPHNhbWwyOklzc3VlciBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpuYW1laWQtZm9ybWF0OmVudGl0eSIgeG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPmh0dHA6Ly93d3cub2t0YS5jb20vZXhrNTEzMjZiM1UxOTQxSGY0eDY8L3NhbWwyOklzc3Vlcj48ZHM6U2lnbmF0dXJlIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj48ZHM6U2lnbmVkSW5mbz48ZHM6Q2Fub25pY2FsaXphdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNyc2Etc2hhMjU2Ii8%2BPGRzOlJlZmVyZW5jZSBVUkk9IiNpZDE4MjkyOTcxMDg4NDIwODczNjgzNTk2ODUyIj48ZHM6VHJhbnNmb3Jtcz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIj48ZWM6SW5jbHVzaXZlTmFtZXNwYWNlcyBQcmVmaXhMaXN0PSJ4cyIgeG1sbnM6ZWM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjwvZHM6VHJhbnNmb3JtPjwvZHM6VHJhbnNmb3Jtcz48ZHM6RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjc2hhMjU2Ii8%2BPGRzOkRpZ2VzdFZhbHVlPjJ0ekZZTktpNTRBVTV0L3R1UUZJS0Q2aHdlWlRqc1FYSTFzWmFreUE3Y0k9PC9kczpEaWdlc3RWYWx1ZT48L2RzOlJlZmVyZW5jZT48L2RzOlNpZ25lZEluZm8%2BPGRzOlNpZ25hdHVyZVZhbHVlPkxJakdOK0RRRExldUE4clF4Q0Yvblp2c3ZCM0VLWjFaUHA3cUU4ZXZZVGY3R01lWGlVa3hXaTkvbGQ1d2p2YWFjaG5ZOXBMV0FObStFRGo4aUhGcnI1a1dINWJZZUI3b0JybVY1NDlTMmpJWW5pbmJBT055T2d3TVRpWXJUQkZ3czg1dHVYb2h0N3JBcVRrWVBjY2FnanJyQW5sYnNyK3BVb3FZYzZMOHZUczR3d2x0MzJiVXF0TThCOG5XS1NmWFBMTEo3VlVGeFhFQm1iV1JsbjJBcjgyN2dhT1dGemoyQXcxWGw2bUZFTllscDVib3hoSlVkRHJDMHNkaW01YXZmQU5jSXFrMjJFQkc5aFRZNUdnbzlrTjBSYkgvYm15MTc5K0lDaWo5R2hlZ2xmWGVpV3pVaWlLUkF1WS8zbEVLZnBYQ2lpaVRVbGdUcHFxdzhnNjNvZz09PC9kczpTaWduYXR1cmVWYWx1ZT48ZHM6S2V5SW5mbz48ZHM6WDUwOURhdGE%2BPGRzOlg1MDlDZXJ0aWZpY2F0ZT5NSUlEcERDQ0FveWdBd0lCQWdJR0FYRWlENExsTUEwR0NTcUdTSWIzRFFFQkN3VUFNSUdTTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHCkExVUVDQXdLUTJGc2FXWnZjbTVwWVRFV01CUUdBMVVFQnd3TlUyRnVJRVp5WVc1amFYTmpiekVOTUFzR0ExVUVDZ3dFVDJ0MFlURVUKTUJJR0ExVUVDd3dMVTFOUFVISnZkbWxrWlhJeEV6QVJCZ05WQkFNTUNtUmxkaTB4TmpJd01qUXhIREFhQmdrcWhraUc5dzBCQ1FFVwpEV2x1Wm05QWIydDBZUzVqYjIwd0hoY05NakF3TXpJNE1UWTFNVFUwV2hjTk16QXdNekk0TVRZMU1qVTBXakNCa2pFTE1Ba0dBMVVFCkJoTUNWVk14RXpBUkJnTlZCQWdNQ2tOaGJHbG1iM0p1YVdFeEZqQVVCZ05WQkFjTURWTmhiaUJHY21GdVkybHpZMjh4RFRBTEJnTlYKQkFvTUJFOXJkR0V4RkRBU0JnTlZCQXNNQzFOVFQxQnliM1pwWkdWeU1STXdFUVlEVlFRRERBcGtaWFl0TVRZeU1ESTBNUnd3R2dZSgpLb1pJaHZjTkFRa0JGZzFwYm1adlFHOXJkR0V1WTI5dE1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBCndzblA0VVRmdjNieFI1SmgwYXQ1MURxamorZkt4RnpuekZXM1hBNU5iRjJTbFJMamVZY3ZqMys0N1RDMGVQNnhPc0xXZm52ZG54NHYKZGQ5VWZuN2pEQ281cEwzSnlrTVZFaDJJMHN6RjNSTEMrYTUzMkFyY3dnVTlQeDQ4K3JXVndQa0FTUzdsNE5IQU00K2dPQkhKTVF0MgpBTW9oUFQwa1U0MVA4QkVQemZ3aE55aUVYUjY2Sk5aSUpVRThmTTNWcGdueG0vVlN3WXpKZjBOZk95Znh2OEpjekYwemtEYnBFN1RrCjNXdy9QRkZMb014V3phbldHSlErYmxuaHY2VVY2SDRmY2ZBYmN3QXBsT2RJVkhqUzJnaFlCdllOR2FodUZ4amlhMCs2Y3N5WkdydDgKSDRYbVI1RHIralhZNUsxYjFWT0EwazE5L0ZDbkhITi9zbW4yNXdJREFRQUJNQTBHQ1NxR1NJYjNEUUVCQ3dVQUE0SUJBUUJnRDlORQo0T0N1UjErdnVjVjhTMVQ2WFhJTDJoQjdiWEJBWkVWSFoxYUVyUnprdGdYQU1nVndHMjY3dklrRDVWT1hCaVR5OXlOVTVMSzZHM2syCnpld1UxOTBzTDFkTWZ5UG5vVlp5bjk0bnZ3ZTlBK29uMHRtWmRtazAweGlyS2szRkpkYWNuWk5FOURsL2FmSXJjTmY2eEFtMFdzVTkKa2JNaVJ3d3ZqTzRUQWl5Z0RRemJyUkM4WmZtVDNocEJhM2FUVXpBY2NydkVRY2dhckxrNHI3VWpYUDdhMm1DTjNVSUloK3NuTjJNcwp2WEhMMHI2Zk0zeGJuaXorNWxsZVd0UEZ3NzN5eVNCYzh6bmtXWjRUbjhMaDByNm81bkNSWWJyMlJFVUI3WklmaUl5QmJaeElwNGt2CmEraGFiYm5RREZpTlZ6RWQ4T1BYSGg0RXFMeE9QRFJXPC9kczpYNTA5Q2VydGlmaWNhdGU%2BPC9kczpYNTA5RGF0YT48L2RzOktleUluZm8%2BPC9kczpTaWduYXR1cmU%2BPHNhbWwyOlN1YmplY3QgeG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPjxzYW1sMjpOYW1lSUQgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjE6bmFtZWlkLWZvcm1hdDp1bnNwZWNpZmllZCI%2Bc2FtQHZjYXJkbWUuY29tPC9zYW1sMjpOYW1lSUQ%2BPHNhbWwyOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj48c2FtbDI6U3ViamVjdENvbmZpcm1hdGlvbkRhdGEgSW5SZXNwb25zZVRvPSJfNTRjNGYxOTEtMWJmNy00YjAyLTk2ZWEtMzRkODU1NWFlY2FhIiBOb3RPbk9yQWZ0ZXI9IjIwMjAtMDQtMTFUMTY6Mzc6MTguNDI0WiIgUmVjaXBpZW50PSJodHRwOi8vbG9jYWxob3N0OjkyOTIvYXV0aC9zYW1sLzY4ZTkwNTdjLTQ2MmYtNDM3Zi04NDRkLTk4NzI0ZmVmOWQ0My9jYWxsYmFjayIvPjwvc2FtbDI6U3ViamVjdENvbmZpcm1hdGlvbj48L3NhbWwyOlN1YmplY3Q%2BPHNhbWwyOkNvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDIwLTA0LTExVDE2OjI3OjE4LjQyNFoiIE5vdE9uT3JBZnRlcj0iMjAyMC0wNC0xMVQxNjozNzoxOC40MjRaIiB4bWxuczpzYW1sMj0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiI%2BPHNhbWwyOkF1ZGllbmNlUmVzdHJpY3Rpb24%2BPHNhbWwyOkF1ZGllbmNlPjY4ZTkwNTdjLTQ2MmYtNDM3Zi04NDRkLTk4NzI0ZmVmOWQ0Mzwvc2FtbDI6QXVkaWVuY2U%2BPC9zYW1sMjpBdWRpZW5jZVJlc3RyaWN0aW9uPjwvc2FtbDI6Q29uZGl0aW9ucz48c2FtbDI6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDIwLTA0LTExVDE2OjMyOjE4LjQyNFoiIFNlc3Npb25JbmRleD0iXzU0YzRmMTkxLTFiZjctNGIwMi05NmVhLTM0ZDg1NTVhZWNhYSIgeG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPjxzYW1sMjpBdXRobkNvbnRleHQ%2BPHNhbWwyOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkUHJvdGVjdGVkVHJhbnNwb3J0PC9zYW1sMjpBdXRobkNvbnRleHRDbGFzc1JlZj48L3NhbWwyOkF1dGhuQ29udGV4dD48L3NhbWwyOkF1dGhuU3RhdGVtZW50PjxzYW1sMjpBdHRyaWJ1dGVTdGF0ZW1lbnQgeG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPjxzYW1sMjpBdHRyaWJ1dGUgTmFtZT0iZW1haWwiIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6dW5zcGVjaWZpZWQiPjxzYW1sMjpBdHRyaWJ1dGVWYWx1ZSB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhzaTp0eXBlPSJ4czpzdHJpbmciPnNhbUB2Y2FyZG1lLmNvbTwvc2FtbDI6QXR0cmlidXRlVmFsdWU%2BPC9zYW1sMjpBdHRyaWJ1dGU%2BPHNhbWwyOkF0dHJpYnV0ZSBOYW1lPSJpZCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDp1bnNwZWNpZmllZCI%2BPHNhbWwyOkF0dHJpYnV0ZVZhbHVlIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeHNpOnR5cGU9InhzOnN0cmluZyI%2BMDB1NTEwd2pwNDRmdDNEUm80eDY8L3NhbWwyOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDI6QXR0cmlidXRlPjwvc2FtbDI6QXR0cmlidXRlU3RhdGVtZW50Pjwvc2FtbDI6QXNzZXJ0aW9uPjwvc2FtbDJwOlJlc3BvbnNlPg%3D%3D&RelayState=
9
- headers:
10
- Host:
11
- - localhost:9292
12
- Connection:
13
- - keep-alive
14
- Cache-Control:
15
- - max-age=0
16
- Upgrade-Insecure-Requests:
17
- - '1'
18
- Origin:
19
- - 'null'
20
- User-Agent:
21
- - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML,
22
- like Gecko) Chrome/80.0.3987.163 Safari/537.36
23
- Sec-Fetch-Dest:
24
- - document
25
- Accept:
26
- - text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
27
- Sec-Fetch-Site:
28
- - cross-site
29
- Sec-Fetch-Mode:
30
- - navigate
31
- Accept-Encoding:
32
- - gzip, deflate, br
33
- Accept-Language:
34
- - en-US,en;q=0.9,fr;q=0.8
35
- Cookie:
36
- - _vcardme-api_session=YmdCdkxYdTlLWkhhWWNnZTQ5Yjdxc0gweWJjKys4TUtSWWVBd3c1U2tUT2E1b2t0MFQxd3lZQTJwSVcyZVFCOHZxaHRtTVZjVGYyay9ReVc0WElxR0hlVGM0MW5qQUxVZE8zYytTaXBlcDZraWRLSllkYjlvYUNMVkx6S3Aremh4ODB5eVBzMmFwS3dHOHNZaFFBYnJnPT0tLVRKUEdSMFV1SGRCZSt1LzNiYjRSZnc9PQ%3D%3D--4e212108b448172e97b0e205f75976780e843bd7;
37
- mp_321622acf650be9cac3978565e73be1a_mixpanel=%7B%22distinct_id%22%3A%20%2217122c6e1421dd-0fd311181d4582-396f7f07-384000-17122c6e14935d%22%2C%22%24device_id%22%3A%20%2217122c6e1421dd-0fd311181d4582-396f7f07-384000-17122c6e14935d%22%2C%22%24initial_referrer%22%3A%20%22%24direct%22%2C%22%24initial_referring_domain%22%3A%20%22%24direct%22%7D;
38
- rack.session=BAh7CUkiD3Nlc3Npb25faWQGOgZFVEkiRTdkYWNhMWRhNjc3MTY4OTY0ZWVj%0AZjg2YmIyOTM1M2EzOTcxZWIzN2ZjMzAxZGUxNmIyZGNiNDdhMGU0ZDdiMmQG%0AOwBGSSINc2FtbF91aWQGOwBUSSIUc2FtQHZjYXJkbWUuY29tBjsAVEkiF3Nh%0AbWxfc2Vzc2lvbl9pbmRleAY7AFRJIipfOTBjYjc4NjItYTc5My00M2ZjLWFh%0AOWItZDdiMjY0ZWE4NmM2BjsAVEkiFG9tbmlhdXRoLnBhcmFtcwY7AFR7AA%3D%3D%0A--429d063c69188264011d1384cbf24a4b486d2d3a
39
- Version:
40
- - HTTP/1.1
41
- Content-Type:
42
- - application/x-www-form-urlencoded
43
- Content-Length:
44
- - '10749'
45
- response:
46
- status:
47
- code: 200
48
- message: null
49
- headers:
50
- Content-Type:
51
- - application/json
52
- Content-Length:
53
- - '112'
54
- body:
55
- encoding: UTF-8
56
- string: '{"user":{"id":"2077ee4f-9e7d-46c2-bbf3-98c49c312281","email":"sam@vcardme.com","idp_id":"00u510wjp44ft3DRo4x6"}}'
57
- http_version: null
58
- recorded_at: Sat, 11 Apr 2020 16:32:18 GMT
59
- recorded_with: VCR 5.1.0