- vừa được xem lúc

Đăng nhập bằng Apple dùng IOS app và Ruby API

0 0 16

Người đăng: Ngeng

Theo Viblo Asia

Để có thể đăng nhập bằng apple sẽ có 2 bước. Thứ nhất là bên mobile sẽ tương tác với apple để lấy token xong request lên Ruby API kèm theo token. Thứ hai, sau khi nhận được request bên server sẽ phần tích token để lấy những thông tin cần thiết và thực hiện đăng nhập.

1. Mobile app tương tác với bên apple để lấy token

Để đăng nhập với apple trước hết bạn cần vào dự án Project -> Signing & Capabilities xong bất lên Sign in With Apple . Và sau đây là những dòng code để lấy apple token :

//
// AppleSignInViewController.swift
//
// Created by Rasmus Styrk on 26/11/2019.
// Copyright © 2019 House of Code ApS. All rights reserved.
//
import UIKit
import AuthenticationServices class AppleSignInViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // TODO: Add button to call self.inititateAppleLogin() } func inititateAppleLogin() { if #available(iOS 13.0, *) { let appleIDProvider = ASAuthorizationAppleIDProvider() let request = appleIDProvider.createRequest() request.requestedScopes = [.fullName, .email] let authorizationController = ASAuthorizationController(authorizationRequests: [request]) authorizationController.delegate = self authorizationController.presentationContextProvider = self authorizationController.performRequests() } else { // TODO: Show error? } } func validateWithServer(name: String, userIdentity: String, jwt: String) { // TODO: Send off userIdeneity, name and jwtToken to server for validation print("User: \(userIdentity), with name: \(name) and jwt token: \(jwt)") }
} @available(iOS 13.0, *)
extension AppleSignInViewController: ASAuthorizationControllerDelegate { func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) { if let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential { let userIdentity = appleIDCredential.user var name: String = "No Name" if let fullName = appleIDCredential.fullName { var nameComponents: [String] = [] if let givenName = fullName.givenName { nameComponents.append(givenName) } if let familyName = fullName.familyName { nameComponents.append(familyName) } if nameComponents.count > 0 { name = nameComponents.joined(separator: " ") } } if let jwtData = appleIDCredential.identityToken, let jwtToken = String(data: jwtData, encoding: .utf8) { self.validateWithServer(name: name, userIdentity: userIdentity, jwt: jwtToken) } else { // Show error? Missing jwt token } } } func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) { // Show error }
} @available(iOS 13.0, *)
extension AppleSignInViewController: ASAuthorizationControllerPresentationContextProviding { func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor { return self.view.window! }
}

Chý ý : Trong token sẽ không bao gồm tên của user. Nếu server cần sử dụng đến tên của user thì bên mobile sẽ cần gửi lên.

2. Xử lý đăng nhập ở trên server side

Token gửi lên sẽ là JWT (Json Web Token) . Để phần tích được JWT đơn giản làm mình có thể sử dụng "jwt" gem.

gem 'jwt'

chạy bundle install .

Code dưới đây sẽ cho bạn lấy được những thông tin của user từ token

class AppleSignInController < ApplicationController APPLE_PEM_URL = "https://appleid.apple.com/auth/keys" # /api/apple/validate def validate name = params[:name] userIdentity = params[:userIdentity] jwt = params[:jwt] begin header_segment = JSON.parse(Base64.decode64(jwt.split(".").first)) alg = header_segment["alg"] kid = header_segment["kid"] apple_response = Net::HTTP.get(URI.parse(APPLE_PEM_URL)) apple_certificate = JSON.parse(apple_response) keyHash = ActiveSupport::HashWithIndifferentAccess.new(apple_certificate["keys"].select {|key| key["kid"] == kid}[0]) jwk = JWT::JWK.import(keyHash) token_data = JWT.decode(jwt, jwk.public_key, true, {algorithm: alg})[0] if token_data.has_key?("sub") && token_data.has_key?("email") && userIdentity == token_data["sub"] puts "Name: " + name + " is validated." # TODO: Create a user in ur rails app and generate an auth token for future requests. Remember to use the "userIdentity" as the # primary user key - this because the email address will change all the time # TODO: Render response to app else # TODO: Render error to app end rescue StandardError => e # TODO: Render error to app end end
end

Nếu bạn có gặp vấn để Undefined JWK bạn có thể thử sử dụng cách này

class AppleSignInController < ApplicationController APPLE_PEM_URL = "https://appleid.apple.com/auth/keys" # /api/apple/validate def validate name = params[:name] userIdentity = params[:userIdentity] jwt = params[:jwt] begin header_segment = JSON.parse(Base64.decode64(jwt.split(".").first)) alg = header_segment["alg"] kid = header_segment["kid"] apple_response = Net::HTTP.get(URI.parse(APPLE_PEM_URL)) apple_certificate = JSON.parse(apple_response) keyHash = ActiveSupport::HashWithIndifferentAccess.new(apple_certificate["keys"].select {|key| key["kid"] == kid}[0]) key = OpenSSL::PKey::RSA.new key.e = OpenSSL::BN.new(UrlSafeBase64.decode64(keyHash["e"]), 2) key.n = OpenSSL::BN.new(UrlSafeBase64.decode64(keyHash["n"]), 2) token_data = JWT.decode(jwt, jwk.public_key, true, {algorithm: alg})[0] if token_data.has_key?("sub") && token_data.has_key?("email") && userIdentity == token_data["sub"] puts "Name: " + name + " is validated." # TODO: Create a user in ur rails app and generate an auth token for future requests. Remember to use the "userIdentity" as the # primary user key - this because the email address will change all the time # TODO: Render response to app else # TODO: Render error to app end rescue StandardError => e # TODO: Render error to app end end
end

References

Bình luận