Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

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>'
  }]
}

https://gist.github.com/nov/782470463001d644a3ce7063ff916c14

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) {
  }
}

https://gist.github.com/nov/be7df5ec71d7eeb93063d2300f28ab90

補足

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

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

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした