A Sample App working with SwiftUI framework will help tremendously
rajahimanath opened this issue · comments
Thanks for the request. I will update here once we add a sample with SwiftUi.
+1 On this. I've just written an entire application in pure SwiftUI just went to add the Auth only to find that there is no examples for it at all. I probably should have looked at this first XD
+1 to this. As a new dev learning SwiftUI I’m finding it difficult to implement a way to do this.
+1 mil to this from me. waiting for almost a year now.
+1 +1 to this. I tried using SwiftUI protocol UIViewControllerRepresentable to 'wrap' an UIViewController in a SwiftUI page view, but eventually got lost on how to pass it to MSALWebviewParameters. A SwiftUI example will definitely help a lot.
+1 to this.
It will be very helpful as it is a high priority requirement for us..
Referring another thread here
+1
+1 to this too!
I am currently working on a senior design capstone project, and this sample app or tutorial would be very beneficial!
+1 to this..
I have experimented with this a little bit.
I thought I found solution with both the browser and ms authenticator app without even touching UIKit, but it has some problems.
As you may know to support the ms authenticator app you need to have either an AppDelegate or SceneDelegate to handle the open url.
You can have an AppDelegate with swiftui (@UIApplicationDelegateAdaptor in the main App) but the open url method is never called.
The only way that I found to make it work is by using the SwiftUI onOpenUrl method and call MSALPublicClientApplication.handleMSALResponse(url) from there.
There are two problems with this approach:
- handleMSALResponse(URL) method is deprecated, and it tells to instead call the handleMSALResponse(URL, sourceApplication: String?). sourceApplication is not provided by SwiftUI's onOpenUrl callback.
- I get a runtime error after the ms authenticator app returns to my app that says: "SwiftUI: Cannot use Scene methods for URL, NSUserActivity, and other External Events without using SwiftUI Lifecycle. Without SwiftUI Lifecycle, advertising and handling External Events wastes resources, and will have unpredictable results."
Let me know if you have found a different solution.
P.S: the msContext is initialised in the @main app
constructor by simply following the UIKit guide.
Button {
if let view = UIApplication.shared.windows.first?.rootViewController {
let parameters = MSALInteractiveTokenParameters(
scopes: ["XboxLive.signin"],
webviewParameters: MSALWebviewParameters(
authPresentationViewController: view
)
)
//Modify the ui with the progress indicator
msContext.acquireToken(with: parameters) {
(result, ex) in
if let exception = ex {
print("MS auth error: ", exception)
//remove the progress indicator and show the error
return
}
guard let msToken = result?.accessToken, let id = result?.account.identifier else {
//remove the progress indicator and show an error
return
}
//Go ahead and use your id and msToken for whatever you need
}
}
} label: {
HStack(spacing: 5) {
Spacer()
Image("microsoft_signin")
Text("Sign in with Microsoft")
.foregroundColor(.primary)
.font(.system(size: 15, weight: .semibold))
Spacer()
}
}.onOpenURL(perform: { url in
MSALPublicClientApplication.handleMSALResponse(url)
})
I was trying to solve a similar issue the other day, creating a SwiftUI based app from another MS Sample Project https://github.com/Azure-Samples/ms-identity-mobile-apple-swift-objc.
I uploaded my SwiftUI sample here https://github.com/alschmut/MSALSwiftUI. From what I have tested until now, it seems to work fine with the MS Authenticator App. Feel free to have a look :)
+1 for this request. I did see a post in StackOverflow with a similar request (which did get a response), but they all seem to be working around the problem.
+1 for this request. I did see a post in StackOverflow with a similar request (which did get a response), but they all seem to be working around the problem.
That's me! Here's the project.
@arbyruns, have you looked at the @alschmut example which encapsulates all the authentication? I did this all in Android and am now trying to make it work in Swift but am a beginner.
I see the call in @arbyruns:
let webViewParameters = MSALWebviewParameters(authPresentationViewController: self)
And in @alschmut's example:
self.webViewParamaters = MSALWebviewParameters(authPresentationViewController: topViewController)
In @arbyruns it's called within a UIViewController class and his uses:
private func topViewController() -> UIViewController? {
let window = UIApplication.shared.windows.filter { $0.isKeyWindow }.first
let rootVC = window?.rootViewController
return rootVC?.top()
}
It seems like you should be able to abstract it from the UI, but need a callback/observable. Is there something Microsoft needs to do to enable this so we don't need an actual UIViewController in the call anywhere?
Maybe help upvote this :
https://feedback.azure.com/d365community/idea/ed66e827-c725-ec11-b6e6-000d3a4f0789
Since this request has been outstanding for so long, I'm concerned about the level of support moving forward. Maybe I should consider generic oAuth Swift options as B2C supports oAuth2, like https://github.com/OAuthSwift/OAuthSwift and https://github.com/p2/OAuth2
This is also discussed on the MSAL repository where a sample has been shared: AzureAD/microsoft-authentication-library-for-objc#1437 (comment)
@antrix1989, how are we doing with this?
Closed as duplicate.
Status? Can't find duplicate nor any examples other than student pet projects
@antrix1989 "Closed as duplicate."
It would be very helpful to post a link to the original post, so we could follow it.
There was a code from @arbyruns which fitted the bill for me (source here), however it is compatible on older versions of iOS. Here is an updated version for iOS17:
import SwiftUI
import UIKit
import Foundation
import MSAL
class MSALScreenViewModel: ObservableObject, MSALScreenViewModelProtocol{
var uiViewController: MSALScreenViewControllerProtocol? = nil
@Published var accountName: String = ""
@Published var scopes: [String] = []
func loadMSALScreen() {
print(#function)
uiViewController?.loadMSALScreen()
}
func getAccountName() -> String {
print(#function)
return accountName
}
}
struct MSALScreenView_UI: UIViewControllerRepresentable{
@ObservedObject var viewModel: MSALScreenViewModel
func makeUIViewController(context: Context) -> some MSALScreenViewController {
print(#function)
return MSALScreenViewController(viewModel: viewModel)
}
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
print(#function)
}
}
class MSALScreenViewController: UIViewController, MSALScreenViewControllerProtocol {
let kClientId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
let kRedirectUri = "msauth.YourProject://auth"
let kScopes: [String] = ["User.Read"]
let kAuthority = "https://login.microsoftonline.com/yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy"
var uiViewController: MSALScreenViewModelProtocol?
var viewModel: MSALScreenViewModelProtocol
init(viewModel: MSALScreenViewModelProtocol) {
print(#function)
self.viewModel = viewModel
super.init(nibName: nil, bundle: .main)
self.viewModel.uiViewController = self
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
print(#function)
viewModel.loadMSALScreen()
}
func loadMSALScreen() {
let msalModel = MSALScreenViewModel()
do {
let authority = try MSALAuthority(url: URL(string: kAuthority)!)
let pcaConfig = MSALPublicClientApplicationConfig(clientId: kClientId, redirectUri: kRedirectUri, authority: authority)
let application = try MSALPublicClientApplication(configuration: pcaConfig)
let webViewParameters = MSALWebviewParameters(authPresentationViewController: self)
let interactiveParameters = MSALInteractiveTokenParameters(scopes: kScopes, webviewParameters: webViewParameters)
application.acquireToken(with: interactiveParameters) { (result, error) in
guard let result = result else {
if let error = error {
print("Error: \(error.localizedDescription)")
} else {
print("Unknown error occurred")
}
return
}
if let account = result.account.username {
msalModel.accountName = account
msalModel.scopes = result.scopes
if let keyWindow = UIApplication.shared.currentUIWindow() {
keyWindow.rootViewController = UIHostingController(rootView: MicrosoftView())
}
}
}
} catch {
print("\(#function) logging error \(error)")
}
}
}
protocol MSALScreenViewModelProtocol{
var uiViewController: MSALScreenViewControllerProtocol? { get set }
func loadMSALScreen()
func getAccountName() -> String
}
protocol MSALScreenViewControllerProtocol: UIViewController{
var viewModel: MSALScreenViewModelProtocol { get set }
func loadMSALScreen()
}
struct MicrosoftView: View {
@StateObject var msalModel: MSALScreenViewModel = MSALScreenViewModel()
@State private var isLoggedIn: Bool = false
var body: some View {
NavigationStack {
VStack {
Spacer()
if isLoggedIn {
List {
NavigationLink(
destination: YourHomeView()
) {
HStack {
Text("Access Home")
.font(.subheadline)
.fontWeight(.bold)
}
}
}
} else {
Button("Login with Microsoft 365") {
msalModel.loadMSALScreen()
}
MSALScreenView_UI(viewModel: msalModel)
.frame(width: 250, height: 250, alignment: .center)
}
}
.onReceive(msalModel.$accountName) { accountName in
isLoggedIn = !accountName.isEmpty
}
}
}
}
public extension UIApplication {
func currentUIWindow() -> UIWindow? {
let connectedScenes = UIApplication.shared.connectedScenes
.filter { $0.activationState == .foregroundActive }
.compactMap { $0 as? UIWindowScene }
let window = connectedScenes.first?
.windows
.first { $0.isKeyWindow }
return window
}
}
#Preview {
MicrosoftView()
}
Closed as duplicate.
@antrix1989 Can you please link the duplicate post? If there is no post, is there an update on a full SwiftUI implementation?