Skip to content
This repository has been archived by the owner on May 6, 2024. It is now read-only.

Commit

Permalink
Social Auth Sign In With Apple Support (#1400)
Browse files Browse the repository at this point in the history
  • Loading branch information
saeedbashir committed Aug 31, 2020
1 parent d549164 commit 20481b3
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 5 deletions.
10 changes: 6 additions & 4 deletions Source/AppleAuthProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@

import UIKit

class AppleAuthProvider: NSObject, OEXExternalAuthProvider {
@objc class AppleAuthProvider: NSObject, OEXExternalAuthProvider {

@objc static let backendName = "apple-id"
override init() {
super.init()
}
Expand All @@ -19,7 +20,7 @@ class AppleAuthProvider: NSObject, OEXExternalAuthProvider {
}

var backendName: String {
return ""
return AppleAuthProvider.backendName
}

func freshAuthButton() -> UIButton {
Expand All @@ -31,7 +32,8 @@ class AppleAuthProvider: NSObject, OEXExternalAuthProvider {
}

func authorizeService(from controller: UIViewController, requestingUserDetails loadUserDetails: Bool, withCompletion completion: @escaping (String?, OEXRegisteringUserDetails?, Error?) -> Void) {
// TODO
return
AppleSocial.shared.loginFromController(controller: controller) { userdetails, token, error in
completion(token,userdetails, error)
}
}
}
118 changes: 118 additions & 0 deletions Source/AppleSocial.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
//
// AppleSocial.swift
// edX
//
// Created by Saeed Bashir on 8/24/20.
// Copyright © 2020 edX. All rights reserved.
//

import AuthenticationServices

private let errorDomain = "AppleSocial"

typealias AppleLoginCompletionHandler = (OEXRegisteringUserDetails?, _ accessToken: String?, Error?) -> Void


class AppleSocial: NSObject {

static let shared = AppleSocial()
private var completionHandler: AppleLoginCompletionHandler?

private override init() {
super.init()
}

func loginFromController(controller : UIViewController, completion: @escaping AppleLoginCompletionHandler) {

completionHandler = completion

if #available(iOS 13.0, *) {
let authorizationProvider = ASAuthorizationAppleIDProvider()
let request = authorizationProvider.createRequest()
request.requestedScopes = [.fullName, .email]

let authorizationController = ASAuthorizationController(authorizationRequests: [request])
authorizationController.delegate = self
authorizationController.presentationContextProvider = self
authorizationController.performRequests()
}
else {
let error = NSError(domain: errorDomain, code: -10000, userInfo: [NSLocalizedDescriptionKey: "Apple Sign In not supported prior to iOS 13"])
completionHandler?(nil, nil, error)
}
}
}

@available(iOS 13.0, *)
extension AppleSocial: ASAuthorizationControllerDelegate {

func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
completionHandler?(nil, nil, error)
}

func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {

guard let credentials = authorization.credential as? ASAuthorizationAppleIDCredential else {
let error = NSError(domain: errorDomain, code: ASAuthorizationError.unknown.rawValue, userInfo: [NSLocalizedDescriptionKey: "Failed to get credentials"])
completionHandler?(nil, nil, error)
return
}

let firstName = credentials.fullName?.givenName
let lastName = credentials.fullName?.familyName
let email = credentials.email ?? ""

let userDetails = OEXRegisteringUserDetails()
userDetails.name = "\(firstName ?? "") \(lastName ?? "")"
userDetails.email = email

if let data = credentials.identityToken, let code = String(data: data, encoding: .utf8) {
if userDetails.email?.isEmpty ?? true {
let tokenDetails = try? decode(jwtToken: code)
userDetails.email = tokenDetails?["email"] as? String ?? ""
}
completionHandler?(userDetails, code, nil)
}
else {
let error = NSError(domain: errorDomain, code: ASAuthorizationError.failed.rawValue, userInfo: [NSLocalizedDescriptionKey: "Unable to extract apple identity token"])
completionHandler?(nil, nil, error)
}
}

private func decode(jwtToken jwt: String) throws -> [String: Any] {

enum DecodeErrors: Error {
case badToken
case other
}

func base64Decode(_ base64: String) throws -> Data {
let padded = base64.padding(toLength: ((base64.count + 3) / 4) * 4, withPad: "=", startingAt: 0)
guard let decoded = Data(base64Encoded: padded) else {
throw DecodeErrors.badToken
}
return decoded
}

func decodeJWTPart(_ value: String) throws -> [String: Any] {
let bodyData = try base64Decode(value)
let json = try JSONSerialization.jsonObject(with: bodyData, options: [])
guard let payload = json as? [String: Any] else {
throw DecodeErrors.other
}
return payload
}

let segments = jwt.components(separatedBy: ".")
return try decodeJWTPart(segments[1])
}

}

@available(iOS 13.0, *)
extension AppleSocial: ASAuthorizationControllerPresentationContextProviding {

func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
return UIApplication.shared.keyWindow!
}
}
2 changes: 1 addition & 1 deletion Source/OEXRegistrationViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ - (void)populateDefaultFormFields {
[fieldController setValue:fieldController.field.defaultValue];
}

if([[fieldController field].name isEqualToString:@"username"]) {
if([[fieldController field].name isEqualToString:@"username"] && ![self.externalProvider.backendName isEqual:AppleAuthProvider.backendName]) {
[fieldController setValue:fieldController.field.defaultValue];
}

Expand Down
4 changes: 4 additions & 0 deletions edX.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
<dict>
<key>aps-environment</key>
<string>development</string>
<key>com.apple.developer.applesignin</key>
<array>
<string>Default</string>
</array>
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:edx.test-app.link</string>
Expand Down
4 changes: 4 additions & 0 deletions edX.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -807,6 +807,7 @@
CBED55012260F90700C4F2F1 /* YoutubeVideoConfigTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBED55002260F90700C4F2F1 /* YoutubeVideoConfigTest.swift */; };
E00523431CF81D9800B7F5C3 /* DiscussionBlockViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E00523421CF81D9800B7F5C3 /* DiscussionBlockViewController.swift */; };
E00523451CFD768700B7F5C3 /* DiscussionBlockViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E00523441CFD768700B7F5C3 /* DiscussionBlockViewControllerTests.swift */; };
E006FDDA24F904F9006E91C2 /* AppleSocial.swift in Sources */ = {isa = PBXBuildFile; fileRef = E006FDD924F904F9006E91C2 /* AppleSocial.swift */; };
E008D9061CCE150F007F3643 /* DiscussionResponsesViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E008D9041CCE1506007F3643 /* DiscussionResponsesViewControllerTests.swift */; };
E00A33C11D3CEB3400963D8F /* SnackbarViewsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E00A33C01D3CEB3400963D8F /* SnackbarViewsTests.swift */; };
E01591F21D533F3B00201B15 /* UIViewController+CommonAdditions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E01591F11D533F3B00201B15 /* UIViewController+CommonAdditions.swift */; };
Expand Down Expand Up @@ -1921,6 +1922,7 @@
E006BD1422DF29D100AA5185 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.json; name = "pt-BR"; path = "pt-BR.lproj/profiles.json"; sourceTree = "<group>"; };
E006BD1522DF29D100AA5185 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.json; name = "pt-BR"; path = "pt-BR.lproj/languages.json"; sourceTree = "<group>"; };
E006BD1622DF29D200AA5185 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.json; name = "pt-BR"; path = "pt-BR.lproj/whats_new.json"; sourceTree = "<group>"; };
E006FDD924F904F9006E91C2 /* AppleSocial.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppleSocial.swift; sourceTree = "<group>"; };
E008D9041CCE1506007F3643 /* DiscussionResponsesViewControllerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DiscussionResponsesViewControllerTests.swift; sourceTree = "<group>"; };
E00A33C01D3CEB3400963D8F /* SnackbarViewsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SnackbarViewsTests.swift; sourceTree = "<group>"; };
E01287DD22EEDD290093285E /* de */ = {isa = PBXFileReference; lastKnownFileType = text.json; name = de; path = de.lproj/subjects.json; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3235,6 +3237,7 @@
8FE04B4A1A1E24C7007F88B8 /* SocialLogin */ = {
isa = PBXGroup;
children = (
E006FDD924F904F9006E91C2 /* AppleSocial.swift */,
8F562F861A1F2D2C00320DB3 /* OEXGoogleSocial.h */,
8F562F871A1F2D2C00320DB3 /* OEXGoogleSocial.m */,
8FE04B4B1A1E2637007F88B8 /* OEXFBSocial.h */,
Expand Down Expand Up @@ -4912,6 +4915,7 @@
9ED168AE1B29A4ED00AA7B5B /* UserAPI.swift in Sources */,
7725016A1C80FB5C002549A9 /* ServerChangedChecker.swift in Sources */,
E07107931C7591360004B76C /* DiscussionHelper.swift in Sources */,
E006FDDA24F904F9006E91C2 /* AppleSocial.swift in Sources */,
77092C751B42E4C1004AA1A1 /* UIStatusBarStyle+Styles.swift in Sources */,
191A002E19405E97004F7902 /* OEXLatestUpdates.m in Sources */,
1AFEB1B61BBD5B95004C471D /* ProfilePictureTaker.swift in Sources */,
Expand Down

0 comments on commit 20481b3

Please sign in to comment.