Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Sign In with Apple (client + server). Петр Третьяков

CocoaHeads
November 15, 2019

Sign In with Apple (client + server). Петр Третьяков

Посмотрим как реализовать стандартный флоу Sign In with Apple в приложении, разберемся с кнопкой и остальным UI, изучим данные, которые нам отдает Apple о пользователе, а затем реализуем серверную часть авторизации.

CocoaHeads

November 15, 2019
Tweet

More Decks by CocoaHeads

Other Decks in Technology

Transcript

  1. UI UIControl import AuthenticationServices (там же, где Keychain Passwords) Локализация

    встроена Есть несколько вариантов UI для кнопки 09 Sign In with Apple
  2. let appleButton = ASAuthorizationAppleIDButton( type: .signIn, style: .black ) appleButton.addTarget(

    self, action: #selector(signInWithApple), for: .touchUpInside ) view.addSubview(appleButton) UI Sign In with Apple 10
  3. let appleIDProvider = ASAuthorizationAppleIDProvider() let appleIDRequest = appleIDProvider.createRequest() appleIDRequest.requestedScopes =

    [ .fullName, .email ] let authorizationController = ASAuthorizationController( authorizationRequests: [ appleIDRequest ] ) authorizationController.delegate = self authorizationController.presentationContextProvider = self authorizationController.performRequests() Flow 13 Sign In with Apple
  4. NSObject, ASAuthorizationControllerDelegate optional func authorizationController( controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization

    ) optional func authorizationController( controller: ASAuthorizationController, didCompleteWithError error: Error ) Delegate Sign In with Apple 14
  5. NSObject, ASAuthorizationControllerPresentationContextProviding func presentationAnchor( for controller: ASAuthorizationController ) !-> ASPresentationAnchor

    { return view.window !?? UIWindow() } typealias ASPresentationAnchor = UIWindow Presentation Context Provider Sign In with Apple 15
  6. let passwordProvider = ASAuthorizationPasswordProvider() let passwordRequest = passwordProvider.createRequest() let authorizationController

    = ASAuthorizationController( authorizationRequests: [ appleIDRequest, passwordRequest ] ) Password Request 17 Sign In with Apple
  7. open var provider: ASAuthorizationProvider { get } open var credential:

    ASAuthorizationCredential { get } ASAuthorization Handle AppleID Request 19 Sign In with Apple
  8. user = ID пользователя (String) authorizationCode = код авторизации (String)

    email = это понятно (String) fullName = набор String? (struct PersonNameComponents?) identityToken = JWT (String) realUserStatus = .unsupported, .unknown, .likelyReal (enum ASUserDetectionStatus) ASAuthorizationAppleIDCredential Handle AppleID Request 20 Sign In with Apple
  9. open func getCredentialState( forUserID userID: String, completion: @escaping ( ASAuthorizationAppleIDProvider.CredentialState,

    Error? ) !-> Void ) public enum CredentialState: Int { case revoked, authorized, notFound, transferred } ASAuthorizationAppleIDProvider Handle AppleID Request 21 Sign In with Apple
  10. Набор символов (483634.8376cf02b4f5437aa966e4961b7fda36.1829) Основной идентификатор пользователя для вашего сервера Не

    меняется внутри приложений одной Development Team Не меняется, если пользователь деавторизуется и снова зайдет через Sign In with Apple User Sign In with Apple 22
  11. { "iss": "https:!//appleid.apple.com", "aud": "Bundle ID", "exp": 1569350740, "iat": 1569350140,

    "sub": "483634.8376cf02b4f5437aa966e4961b7fda36.1829", "c_hash": "c73hOYjSygNphySkdygIyw", "email": "[email protected]", "email_verified": "true", "auth_time": 1569350140 } Identity Token Payload 25 Sign In with Apple
  12. Identifiers — Services IDs Создать сервис ID Добавить домен и

    подтвердить его 2 Developer Portal 27 Sign In with Apple
  13. Certificates — Keys Создать ключ, привязанный к сервису Сохранить приватный

    ключ, НЕ ПРОФАКАПИТЬ Key ID 3 Developer Portal 29 Sign In with Apple
  14. Certificates — More — Configure Sign In with Apple Добавить

    домен (уже подтвержден) Добавить почтовые адреса 4 Developer Portal 31 Sign In with Apple
  15. Identity Token (app) Authorization Key (app) Private Key (Developer Portal)

    Key ID (Developer Portal) Team ID Bundle ID Что у нас есть? 33 Sign In with Apple
  16. user = ID пользователя (String) authorizationCode = код авторизации (String)

    email = это понятно (String) fullName = набор String? (struct PersonNameComponents?) identityToken = JWT (String) realUserStatus = .unsupported, .unknown, .likelyReal (enum ASUserDetectionStatus) ASAuthorizationAppleIDCredential Credential 34 Sign In with Apple
  17. user = ID пользователя (String) authorizationCode = код авторизации (String)

    email = это понятно (String) fullName = набор String? (struct PersonNameComponents?) identityToken = JWT (String) realUserStatus = .unsupported, .unknown, .likelyReal (enum ASUserDetectionStatus) ASAuthorizationAppleIDCredential Credential 35 Sign In with Apple
  18. GET https:!//appleid.apple.com/auth/keys { "keys": [ { "kty": "RSA", "kid": "AIDOPK1",

    "use": "sig", "alg": "RS256", "n": “lxr!..ОЧЕНЬ ДЛИННАЯ СТРОКА!..lE-w”, "e": "AQAB" } ] } Валидация ID токена 36 Sign In with Apple
  19. JWT.decode( identity_token, nil, true, { iss: 'https:!//appleid.apple.com', verify_iss: true, aud:

    'Bundle ID’, verify_aud: true, algorithms: [‘RS256'], jwks: jwks } ) Валидация ID токена 37 Sign In with Apple
  20. Создать подпись из Private ключа Подписать ей JWT с Team

    ID, Bundle ID, Key ID Отправить этот токе с ключом авторизации на сервер Apple В ответ придут Access и Refresh токены, а также Identity Token Получение Access токена 38 Sign In with Apple
  21. key_file = 'PrivateKey.p8' team_id = 'Team ID' bundle_id = 'Bundle

    ID' key_id = 'Key ID from Developer Portal' ecdsa_key = OpenSSL!::PKey!::EC.new IO.read key_file Получение Access токена 39 Sign In with Apple
  22. headers = { alg: 'ES256', kid: key_id } payload =

    { iss: team_id, iat: Time.now.to_i, exp: Time.now.to_i + 86400*180, aud: 'https:!//appleid.apple.com', sub: bundle_id, } JWT.encode payload, ecdsa_key, 'ES256', headers Создание токена 40 Sign In with Apple
  23. POST https:!//appleid.apple.com/auth/token { "client_id": “Bundle ID", "client_secret": “Generated Token", "grant_type":

    "authorization_code", "redirect_uri": “Redirect URI from Service ID”, "code": “Authorization Code" } Авторизация 41 Sign In with Apple
  24. { "access_token": "Access Token", "token_type": "Bearer", "expires_in": 3600, "refresh_token": “Refresh

    Token", "id_token": “Identity Token" } Авторизация 42 Sign In with Apple
  25. (Reserved for future use) A token used to access allowed

    data. Currently, no data set has been defined for access. Как использовать Access Token? 44 Sign In with Apple
  26. Посмотреть все приложения, где вы авторизовались Посмотреть свой фэйковый email

    Запретить отправку писем на email Деавторизоваться из приложения (revoke token) https://appleid.apple.com/ Sign Out from Apple 45 Sign In with Apple