Class: StandardSingpass::Myinfo::Configuration
- Inherits:
-
Object
- Object
- StandardSingpass::Myinfo::Configuration
- Defined in:
- lib/standard_singpass/myinfo/configuration.rb
Constant Summary collapse
- DEFAULT_SCOPE =
Default MyInfo scope — keep aligned with the Singpass developer-portal approval list. Entries on separate lines so diffs against the portal’s ordered dump are reviewable line-by-line. Joined into a single space-delimited string before sending to PAR.
%w[ openid aliasname cpfbalances.oa cpfcontributions cpfemployers cpfhousingwithdrawal dob email employment employmentsector hanyupinyinaliasname hanyupinyinname hdbownership.address hdbownership.balanceloanrepayment hdbownership.hdbtype hdbownership.loangranted hdbownership.monthlyloaninstalment hdbownership.noofowners hdbownership.outstandinginstalment hdbownership.outstandingloanbalance hdbtype housingtype marital marriedname mobileno name nationality noa noa-basic noahistory noahistory-basic occupation ownerprivate passexpirydate passstatus passtype race regadd residentialstatus sex uinfin vehicles.effectiveownership ].join(" ").freeze
- PRODUCTION_ENDPOINTS =
The categories and their lender-underwriting purpose (PDPA §18, Purpose Limitation):
Identity — uinfin, name, alias names, dob, sex, race, nationality, residentialstatus → KYC, contracts Pass (FIN-only) — passtype, passstatus, passexpirydate, employmentsector → tenure-vs-pass-expiry, eligibility Address — regadd, hdbtype, housingtype → KYC + income proxy Contact — mobileno, email → OTP, mailers Family — marital → soft underwriting signal Income — noa, noa-basic, noahistory, noahistory-basic, cpfcontributions → MAS TDSR input Employment — employment, occupation, cpfemployers → continuity + employer stability Assets — cpfbalances.oa (only OA — MA/SA/RA are ring-fenced and not lender-relevant), ownerprivate Liabilities — cpfhousingwithdrawal, hdbownership.* (8 sub-fields) → TDSR housing component Vehicle — vehicles.effectiveownership (asset/liability hint; full vehicle details deliberately not requested)‘cpfbalances.oa`, `hdbownership.*`, and `vehicles.effectiveownership` use FAPI 2.0 sub-attribute scope notation — sharper data minimisation than parent-keyword grants.
{ authorize_url: "https://id.singpass.gov.sg/fapi/auth", par_url: "https://id.singpass.gov.sg/fapi/par", token_url: "https://id.singpass.gov.sg/fapi/token", jwks_url: "https://id.singpass.gov.sg/.well-known/keys", issuer: "https://id.singpass.gov.sg/fapi", userinfo_url: "https://id.singpass.gov.sg/fapi/userinfo", userinfo_jwks_url: "https://id.singpass.gov.sg/.well-known/keys" }.freeze
- STAGING_ENDPOINTS =
{ authorize_url: "https://stg-id.singpass.gov.sg/fapi/auth", par_url: "https://stg-id.singpass.gov.sg/fapi/par", token_url: "https://stg-id.singpass.gov.sg/fapi/token", jwks_url: "https://stg-id.singpass.gov.sg/.well-known/keys", issuer: "https://stg-id.singpass.gov.sg/fapi", userinfo_url: "https://stg-id.singpass.gov.sg/fapi/userinfo", userinfo_jwks_url: "https://stg-id.singpass.gov.sg/.well-known/keys" }.freeze
Instance Attribute Summary collapse
-
#authorize_url ⇒ Object
Returns the value of attribute authorize_url.
-
#client_id ⇒ Object
Returns the value of attribute client_id.
-
#encryption_keys ⇒ Object
Returns the value of attribute encryption_keys.
-
#environment ⇒ Object
Returns the value of attribute environment.
-
#issuer ⇒ Object
Returns the value of attribute issuer.
-
#jwks_url ⇒ Object
Returns the value of attribute jwks_url.
-
#minimum_acr ⇒ Object
Returns the value of attribute minimum_acr.
-
#mock_mode ⇒ Object
Returns the value of attribute mock_mode.
-
#network_wrapper ⇒ Object
Returns the value of attribute network_wrapper.
-
#par_url ⇒ Object
Returns the value of attribute par_url.
-
#personas_path ⇒ Object
Returns the value of attribute personas_path.
-
#redirect_url ⇒ Object
Returns the value of attribute redirect_url.
-
#scope ⇒ Object
Returns the value of attribute scope.
-
#signing_key ⇒ Object
Returns the value of attribute signing_key.
-
#signing_kid ⇒ Object
Returns the value of attribute signing_kid.
-
#token_url ⇒ Object
Returns the value of attribute token_url.
-
#userinfo_jwks_url ⇒ Object
Returns the value of attribute userinfo_jwks_url.
-
#userinfo_url ⇒ Object
Returns the value of attribute userinfo_url.
Instance Method Summary collapse
-
#initialize ⇒ Configuration
constructor
A new instance of Configuration.
-
#private_jwks_json=(jwks_json) ⇒ Object
Accepts the raw JWKS JSON string and populates signing_key, signing_kid, and encryption_keys.
Constructor Details
#initialize ⇒ Configuration
Returns a new instance of Configuration.
128 129 130 131 132 133 134 |
# File 'lib/standard_singpass/myinfo/configuration.rb', line 128 def initialize self.environment = :staging @scope = DEFAULT_SCOPE @encryption_keys = [] @network_wrapper = ->(&block) { block.call } @mock_mode = false end |
Instance Attribute Details
#authorize_url ⇒ Object
Returns the value of attribute authorize_url.
122 123 124 |
# File 'lib/standard_singpass/myinfo/configuration.rb', line 122 def @authorize_url end |
#client_id ⇒ Object
Returns the value of attribute client_id.
122 123 124 |
# File 'lib/standard_singpass/myinfo/configuration.rb', line 122 def client_id @client_id end |
#encryption_keys ⇒ Object
Returns the value of attribute encryption_keys.
122 123 124 |
# File 'lib/standard_singpass/myinfo/configuration.rb', line 122 def encryption_keys @encryption_keys end |
#environment ⇒ Object
Returns the value of attribute environment.
148 149 150 |
# File 'lib/standard_singpass/myinfo/configuration.rb', line 148 def environment @environment end |
#issuer ⇒ Object
Returns the value of attribute issuer.
122 123 124 |
# File 'lib/standard_singpass/myinfo/configuration.rb', line 122 def issuer @issuer end |
#jwks_url ⇒ Object
Returns the value of attribute jwks_url.
122 123 124 |
# File 'lib/standard_singpass/myinfo/configuration.rb', line 122 def jwks_url @jwks_url end |
#minimum_acr ⇒ Object
Returns the value of attribute minimum_acr.
122 123 124 |
# File 'lib/standard_singpass/myinfo/configuration.rb', line 122 def minimum_acr @minimum_acr end |
#mock_mode ⇒ Object
Returns the value of attribute mock_mode.
122 123 124 |
# File 'lib/standard_singpass/myinfo/configuration.rb', line 122 def mock_mode @mock_mode end |
#network_wrapper ⇒ Object
Returns the value of attribute network_wrapper.
122 123 124 |
# File 'lib/standard_singpass/myinfo/configuration.rb', line 122 def network_wrapper @network_wrapper end |
#par_url ⇒ Object
Returns the value of attribute par_url.
122 123 124 |
# File 'lib/standard_singpass/myinfo/configuration.rb', line 122 def par_url @par_url end |
#personas_path ⇒ Object
Returns the value of attribute personas_path.
122 123 124 |
# File 'lib/standard_singpass/myinfo/configuration.rb', line 122 def personas_path @personas_path end |
#redirect_url ⇒ Object
Returns the value of attribute redirect_url.
122 123 124 |
# File 'lib/standard_singpass/myinfo/configuration.rb', line 122 def redirect_url @redirect_url end |
#scope ⇒ Object
Returns the value of attribute scope.
122 123 124 |
# File 'lib/standard_singpass/myinfo/configuration.rb', line 122 def scope @scope end |
#signing_key ⇒ Object
Returns the value of attribute signing_key.
122 123 124 |
# File 'lib/standard_singpass/myinfo/configuration.rb', line 122 def signing_key @signing_key end |
#signing_kid ⇒ Object
Returns the value of attribute signing_kid.
122 123 124 |
# File 'lib/standard_singpass/myinfo/configuration.rb', line 122 def signing_kid @signing_kid end |
#token_url ⇒ Object
Returns the value of attribute token_url.
122 123 124 |
# File 'lib/standard_singpass/myinfo/configuration.rb', line 122 def token_url @token_url end |
#userinfo_jwks_url ⇒ Object
Returns the value of attribute userinfo_jwks_url.
122 123 124 |
# File 'lib/standard_singpass/myinfo/configuration.rb', line 122 def userinfo_jwks_url @userinfo_jwks_url end |
#userinfo_url ⇒ Object
Returns the value of attribute userinfo_url.
122 123 124 |
# File 'lib/standard_singpass/myinfo/configuration.rb', line 122 def userinfo_url @userinfo_url end |
Instance Method Details
#private_jwks_json=(jwks_json) ⇒ Object
Accepts the raw JWKS JSON string and populates signing_key, signing_kid, and encryption_keys. Logs and reports issues via Rails.logger / Rails.error rather than raising — a malformed JWKS silently degrades the Singpass widget at runtime, but the host should boot regardless.
154 155 156 157 158 159 160 161 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 199 200 201 202 203 204 205 206 207 208 209 210 |
# File 'lib/standard_singpass/myinfo/configuration.rb', line 154 def private_jwks_json=(jwks_json) @encryption_keys = [] @signing_key = nil @signing_kid = nil if jwks_json.nil? || jwks_json.to_s.strip.empty? return if mock_mode || (defined?(Rails) && Rails.env.test?) Rails.logger.warn("StandardSingpass::Myinfo: private_jwks_json is not set — Singpass flow will fail at first request") return end jwks = JSON.parse(jwks_json) raise TypeError, "private_jwks_json must be a JSON object with a \"keys\" array, got #{jwks.class}" unless jwks.is_a?(Hash) keys = jwks["keys"] || [] sig_jwks = keys.select { |k| k.is_a?(Hash) && k["use"] == "sig" } Rails.logger.warn("StandardSingpass::Myinfo: multiple sig keys in private_jwks_json — using first") if sig_jwks.size > 1 sig_jwk = sig_jwks.first if sig_jwk @signing_kid = sig_jwk["kid"] @signing_key = jwk_to_private_pem(sig_jwk, role: "signing") # Keep paired: a nil signing_key with a populated signing_kid is # confusing in console triage (which is the scenario this method is # trying to help with). @signing_kid = nil unless @signing_key elsif !mock_mode && !(defined?(Rails) && Rails.env.test?) Rails.logger.error("StandardSingpass::Myinfo: private_jwks_json contains no key with \"use\":\"sig\"") end enc_jwks = keys.select { |k| k.is_a?(Hash) && k["use"] == "enc" } @encryption_keys = enc_jwks.filter_map do |enc_jwk| pem = jwk_to_private_pem(enc_jwk, role: "encryption") next unless pem { kid: enc_jwk["kid"], key: pem } end # Distinguish the two empty-state cases: missing entirely (operator # forgot to include enc keys) vs all-rejected (every enc key was # public-only or otherwise unloadable). The latter is the trap the # rest of this method is built to catch. if @encryption_keys.empty? && !mock_mode && !(defined?(Rails) && Rails.env.test?) if enc_jwks.empty? Rails.logger.error("StandardSingpass::Myinfo: private_jwks_json contains no key with \"use\":\"enc\"") else Rails.logger.error("StandardSingpass::Myinfo: private_jwks_json has \"use\":\"enc\" keys but none are usable (all public-only or invalid)") end end rescue JSON::ParserError, TypeError => e # JSON::ParserError: not valid JSON. TypeError: valid JSON but wrong # shape (e.g. an array, a string) — caught explicitly so an operator # who pastes the wrong file doesn't see a bare TypeError escape. # Reported because a malformed private JWKS silently degrades the # Singpass widget — the request that finally fails will report, but by # then customers have hit the broken page. Rails.logger.error("StandardSingpass::Myinfo: failed to parse private_jwks_json: #{e.class}: #{e.}") Rails.error.report(e, handled: true, context: { component: "StandardSingpass::Myinfo::Configuration", reason: "parse_private_jwks" }) if defined?(Rails.error) @encryption_keys = [] end |