Class: OmniAuth::Strategies::Realme

Inherits:
Object
  • Object
show all
Includes:
OmniAuth::Strategy
Defined in:
lib/omniauth/strategies/realme.rb

Defined Under Namespace

Classes: Error, RealmeAuthnFailedError, RealmeInternalError, RealmeNoAvailableIDPError, RealmeNoPassiveError, RealmeRequestDeniedError, RealmeRequestUnsupportedError, RealmeTimeoutError, RealmeUnknownPrincipalError, RealmeUnrecognisedError, RealmeUnsupportedBindingError, RelayStateTooLongError

Constant Summary collapse

RCMS_LAT_NAME =
'urn:nzl:govt:ict:stds:authn:safeb64:logon_attributes_jwt'
MAX_LENGTH_OF_RELAY_STATE =

The SAML spec says the maximum length of the RelayState is 80 bytes. See section 3.4.3 of docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf

80

Instance Method Summary collapse

Instance Method Details

#callback_phaseObject

rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/AbcSize



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/omniauth/strategies/realme.rb', line 72

def callback_phase # rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/AbcSize
  response = ::OneLogin::RubySaml::Response.new(request.params['SAMLResponse'],
                                                settings: saml_settings,
                                                allowed_clock_drift: allowed_clock_drift)

  ##
  # `RelayState` is an arbitrary string (length < 80 characters). If we
  # sent it to Realme with the SAMLRequest then Realme will return it unaltered.
  #
  # If we receive any relay state then we save it.
  #
  @relay_state = request.params['RelayState'] if request.params['RelayState']

  # If the Realme Context Mapping Service (RCMS) is enabled in Realme
  # for our app then we will get a RCMS Login Access Token in the
  # SAMLResponse.
  #
  # We save the token if it exists. See
  # https://developers.realme.govt.nz/how-realme-works/whats-realme-rcms/
  #
  if response.is_valid?
    @realme_cms_lat = response.attributes[RCMS_LAT_NAME] if response.attributes[RCMS_LAT_NAME]
  end

  if legacy_rails_session_behaviour_enabled?
    OmniAuth.logger.info "Deprecation: omniauth-realme will stop putting values via Rails `session` in a future version. Use request.env['omniauth.auth'] instead." # rubocop:disable Layout/LineLength

    if response.is_valid?
      session[:uid] = response.nameid
    else
      session[:realme_error] = {
        error: response.errors.join[/=> (\S+) ->/, 1],
        message: default_error_messages_for_rails_session(response.errors.join)
      }
    end
  else
    if response.is_valid? # rubocop:disable Style/IfInsideElse
      @uid = response.nameid
    else
      msg = response.status_message ? response.status_message.strip : ''
      ex = create_exception_for(status_code: response.status_code, message: msg)

      # fail!() returns a rack response which this callback must also
      # return if OmniAuth error handling is to work correctly.
      return fail!(create_label_for(ex), ex)
    end
  end

  super
end

#request_phaseObject



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/omniauth/strategies/realme.rb', line 44

def request_phase
  req_options = { 'SigAlg' => 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256' }

  ##
  # If we recieved a `relay_state` param e.g. we were invoked like:
  #
  #   redirect_to user_realme_omniauth_authorize_path(relay_state: 'some_value')
  #
  # then we pass it to Realme (via RubySaml). Realme (as a SAML IdP)
  # should return that value unaltered when it redirects back to this
  # application and `#callback_phase` below is executed.
  #
  if request.params['relay_state']
    if limit_relay_state? && request.params['relay_state'].length > MAX_LENGTH_OF_RELAY_STATE
      ex = RelayStateTooLongError.new('RelayState exceeds SAML spec max length of 80 bytes')

      # fail!() returns a rack response which this callback must also
      # return if OmniAuth error handling is to work correctly.
      return fail!(create_label_for(ex), ex)
    end

    req_options['RelayState'] = request.params['relay_state']
  end

  req = OneLogin::RubySaml::Authrequest.new
  redirect req.create(saml_settings, req_options)
end

#saml_settingsObject

rubocop:disable Metrics/AbcSize



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/omniauth/strategies/realme.rb', line 162

def saml_settings # rubocop:disable Metrics/AbcSize
   = OneLogin::RubySaml::IdpMetadataParser.new
  settings = .parse(File.read(options.fetch('idp_service_metadata')))

  settings.issuer                             = options.fetch('issuer')
  settings.assertion_consumer_service_url     = options.fetch('assertion_consumer_service_url')
  settings.attributes_index                   = options.fetch('attributes_index', '0')
  settings.private_key                        = options.fetch('private_key')
  settings.authn_context                      = options.fetch('auth_strength', 'urn:nzl:govt:ict:stds:authn:deployment:GLS:SAML:2.0:ac:classes:LowStrength')
  settings.protocol_binding                   = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'
  settings.assertion_consumer_service_binding = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'
  settings.soft                               = !options.fetch('raise_exceptions_for_saml_validation_errors', false)

  settings.security[:authn_requests_signed] = true

  ##
  # Realme error if this is missing from the metadata
  #
  #     WantAssertionsSigned must be true (MTS-002)
  #
  settings.security[:want_assertions_signed] = true

  ##
  # Realme MTS requires our Metadata XML to have both:
  #
  #     <md:KeyDescriptor use="signing">...</md:KeyDescriptor>
  #     <md:KeyDescriptor use="encryption">...</md:KeyDescriptor>
  #
  # in the metadata XML we submit. We need to set a certificate **and**
  # set `:want_assertions_encrypted` for ruby-saml to include these
  # elements.
  #
  settings.certificate = options.fetch('certificate')
  settings.security[:want_assertions_encrypted] = true

  settings
end