LoginSignup
2
1

More than 5 years have passed since last update.

SalesForce 用の OpenID Connect IdP Registration Handler & SCIM Client サンプル

Last updated at Posted at 2017-03-30

Gistは埋もれていく & Gistペタペタ系記事はQiitaのが楽なんじゃないか?ということで、久しぶりに Qiita に。

前提

  • Provisioning は SCIM で。
    • JIT Provisioning はしない。
  • Email による紐付けではなく、ちゃんと Federation 用に識別子を発行して紐付けを行う。
    • 再利用の可能性のある識別子を紐付けに使うべきではない。

SalesForce 向けの SCIM Provisioning サンプルコード。

SCIM の External ID に指定した識別子は SalesForce 側の Federation Identifier に入る。

require 'rack/oauth2'

Rack::OAuth2.debug!

client = Rack::OAuth2::Client.new(
  identifier: '<YOUR-CLIENT-ID>',
  secret: '<YOUR-CLIENT-SECRET>',
  authorization_endpoint: 'https://login.salesforce.com/services/oauth2/authorize',
  token_endpoint: 'https://login.salesforce.com/services/oauth2/token',
  redirect_uri: '<YOUR-CALLBACK-URL>'
)

def endpoint_for(resource)
  scim_base_endpoint = 'https://<YOUR-DOMAIN>.my.salesforce.com/services/scim/v1'
  File.join(scim_base_endpoint, resource)
end

module JSONized
  def request_to(resource, method: :get, params: nil)
    response = send method, endpoint_for(resource), params.try(:to_json), 'Content-Type': 'application/json'
    puts JSON.pretty_generate JSON.parse(response.body)
  end
end

authorization_uri = client.authorization_uri(
  scope: [:api]
)
`open "#{authorization_uri}"`

print 'code: ' and STDOUT.flush
code = gets.chop

client.authorization_code = code
token = client.access_token! :body

token.extend JSONized
# token.request_to 'Entitlements' # => ここのレスポンスから適切な Entitlement (e.g. Standard User) の識別子を取得しておく。
token.request_to 'Users', method: :post, params: {
  externalId: '<OIDC-SUBJECT-VALUE>',
  userName: 'some-user@your-idp.example.comp',
  name: {
    familyName: 'User',
    givenName: 'Some'
  },
  emails: [{
    value: 'some-user@your-idp.example.com'
  }],
  entitlements: [{
    value: '<ENTITLEMETN-ID>'
  }]
}

SalesForce の OIDC IdP 向け Registration Handler Apex Script。

Federation Identifier と OIDC の "sub" が一致すれば、その "sub" と当該 SalesForce アカウントを紐付ける。(createUser)
紐付け確定後は特に何もしない。 (updateUser)

public class OIDCRegHandler implements Auth.RegistrationHandler{
  public User createUser(Id portalId, Auth.UserData data){
    List<User> users = [SELECT Id FROM User WHERE FederationIdentifier =:data.identifier];
    if (users.size() == 1) {
      return users[0];
    } else {
      return null;
    }
  }

  public void updateUser(Id userId, Id portalId, Auth.UserData data) {
  }
}

補足

このサンプルでは、OIDC の Issuer はユーザー識別に用いていない。

OIDC は 原理的には Issuer & Subject のペアによりユーザーを一意に識別できることを保証しているのみで、Subject のみでユーザーを一意に識別することは保証しないプロトコルである。(SAML も同様である)

このサンプルは「あなたが当該 SalesForce テナントに設定する OpenID Connect IdP が一つのみであるため、Issuer はチェックせずとも固定値であることが確約されている」ということを前提としている点に注意すること。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1