Closed JolofNar closed 3 years ago
@JolofNar : how can I ramp-up to authentication with Unity (I don't know Unity). Can you help us help you? do you have repro steps, samples that we could see, etc ...
Hi @jmprieur I'm new to using Github like this, so please be patient with me, but I would love to help however I can.
I think to reproduce you would need to set up a build for android in Unity, and create a simple scene with a button then add this script
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using Microsoft.Identity.Client; using System.Windows; using Microsoft.Graph; using System.Linq; using System.Windows.Forms;
public class AadLoginUI : MonoBehaviour {
;
public Button LoginButton;
public static string ClientId = // client/app id from azure
public static string Tenant = // tenant id
private string[] scopes = { "user.read" };
// Use this for initialization
void Start () {
LoginButton.onClick.RemoveAllListeners();
LoginButton.onClick.AddListener(LoginButton_OnCLick);
}
public void LoginButton_OnCLick()
{
try
{
Debug.Log("In authenticateUser method: pre");
AuthenticateUser();
Debug.Log("In authenticateUser method: post");
}
catch (System.Exception ex)
{
StatusText.text = "An Error has occured";
Debug.Log("error1 = "ex.Message);
}
}
public async void AuthenticateUser()
{
Debug.Log("In authenticateUser method");
AuthenticationResult authResult = null;
IPublicClientApplication app = PublicClientApplicationBuilder.Create(ClientId).WithRedirectUri($"msal{ClientId}://auth").Build();
//var app = PublicClientApplicationBuilder.Create(ClientId).Build();
IEnumerable<IAccount> accounts = await app.GetAccountsAsync();
try
{
//IAccount firstAccount = accounts.FirstOrDefault();
authResult = await app.AcquireTokenSilent(scopes, accounts.FirstOrDefault()).ExecuteAsync();
Debug.Log("Got Token from cache...");
}
catch (MsalUiRequiredException ex)
{
try
{
var actClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
var activity = actClass.GetStatic<AndroidJavaObject>("currentActivity");
Debug.Log("Launching interactive login");
authResult = await app.AcquireTokenInteractive(scopes).WithParentActivityOrWindow(activity)
.ExecuteAsync();
Debug.Log("Returned from interactive login");
}
catch (Exception exx)
{
Debug.Log("exx: " + exx);
}
}
if (authResult != null)
{
Debug.Log("username: " + authResult.Account.Username);
}
}
}
Then build out to android, It hits errors as it tries to calltokenasync, as far s I can tell it wants an android activity there, but even with some fumbling around in java I don't seem to be able to paste one in.
@JolofNar : would you have a zip file that you could upload (using "Attach files by dragging & dropping" below a comment, and also a link on how to install the environment (unless this is in Visual Studio). As far as I'm concerned, Just to give you an idea; I did not know that Android Unity existed, and I would not even know how to create a scene (I guess I can look it up, but that's not knowledge I have today)
I will have to make up a project to share, as the one I'm currently working on is from work.
Here is the project file Msal Test.zip
Did you ever get this working? I'm trying to get MSAL working in Unity as well, but I get an exception on AquireTokenInteractive
System.PlatformNotSupportedException: Possible Cause: If you are using an XForms app, or generally a netstandard assembly, make sure you add a reference to Microsoft.Identity.Client.dll from each platform assembly (e.g. UWP, Android, iOS)
I assume I have to include some aar or jar files...
Any help regarding this would really be appreciated
Hi HeinA
This was a while back so I’m not remembering perfectly. But. I think that we couldn’t get MSAL to work the way we wanted for the project. And ended up using OAUTH 2.0.
The issue as I recall was getting the key sent back to the app. And even OAUTH wasn’t super easy. There just isn’t anything specific to Unity that lets you do it without branching into android areas. Although I think it probably is possible (and waaaaay easier) if you know java to build and handle all this stuff in there. And then call the function on the java plugin from unity. And get a string returned.
Again I can’t remember exactly which solution we ended up going with (we had to come up with a bunch of wacky work arounds).
But we looked at opening a web browser in the app. Because then it’s easy to get the key back. But that won’t work for every platform.
We also used device code for a while, while which is pretty easy to get working but doesn’t have a nice user flow at all.
Lastly we had it calling a java plugin to authorise. But had issues getting the key back to the app, I think due to a lack of knowledge with android manifest files.
That is very sad to hear. Thank you for the reply
I cannot verify if this works and I expect it would be a bit complicated; however could try this is you have time: https://docs.unity3d.com/Manual/AndroidAARPlugins.html
MSAL Android is an AAR... so you it looks like you could add it as a plugin... not sure how to invoke it from Unity: https://github.com/AzureAD/microsoft-authentication-library-for-android
Thanx. I'll look at that. I have included that library already to see if it fixes a run time error
@HeinA did you manage to get it to work? We are also trying to use MSAL in Unity for Android and iOS
For people reading this in future, it's possible to use msal for android in Unity. 1) Include msal as an aar plugin (or you can pull it as a gradle dependency using maintemplate.gradle support in Unity). 2) Make an android library that calls msal and returns the access token as string. Unity supports calling Android classes and returning values from them: https://docs.unity3d.com/ScriptReference/AndroidJavaClass.html, https://docs.unity3d.com/ScriptReference/AndroidJavaProxy.html
For initializing msal you need a reference to the Unity activity. On C# side get Unity activity like this:
AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject unityActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
Pass this to java plugin and call getApplicationContext()
to get the context needed to initialize msal.
@ashikns - wouldn't it be easier to use the native MSAL for Android - https://github.com/AzureAD/microsoft-authentication-library-for-android which is written in Java? Or does Unity still need a CLR entry-point?
@bgavrilMS What I wrote is for native msal android. Unity -> proxy class -> native msal is the data flow and reverse for getting access token back in Unity.
Hi everybody, I have been trying to implement MSAL for Android in Unity for over two weeks. But im stuck in creating the proxy class that calls MSAL. I have some questions that maybe @ashikns or someone else could answer me.
My Proxy Class:
package com.iar.mfa;
import android.app.Activity;
import androidx.annotation.NonNull;
import com.microsoft.identity.client.HttpMethod; import com.microsoft.identity.client.IAccount; import com.microsoft.identity.client.IAuthenticationResult; import com.microsoft.identity.client.IMultipleAccountPublicClientApplication; import com.microsoft.identity.client.IPublicClientApplication; import com.microsoft.identity.client.ISingleAccountPublicClientApplication; import com.microsoft.identity.client.Prompt; import com.microsoft.identity.client.PublicClientApplication; import com.microsoft.identity.client.exception.MsalException;
public class Autenticator { private MsalWrapper mMsalWrapper; public String Token;
public String Autenticate(Activity activity) {
Token = "";
MsalWrapper.create(activity,
Constants.getResourceIdFromConfigFile(Constants.ConfigFile.WEBVIEW),
new INotifyOperationResultCallback<MsalWrapper>() {
@Override
public void onSuccess(MsalWrapper result) {
mMsalWrapper = result;
}
@Override
public void showMessage(String message) {
//showMessageWithToast(message);
}
});
INotifyOperationResultCallback acquireTokenCallback = new INotifyOperationResultCallback<IAuthenticationResult>() {
@Override
public void onSuccess(IAuthenticationResult result) {
Token = result.getAccessToken();
}
@Override
public void showMessage(String message) {
//showMessageWithToast(message);
}
};
mMsalWrapper.acquireToken(activity, getCurrentRequestOptions(),acquireTokenCallback);
return Token;
}
public void callback(){
}
private RequestOptions getCurrentRequestOptions() {
final Constants.ConfigFile configFile = Constants.ConfigFile.WEBVIEW;
final String loginHint = "";
final IAccount account = null;
final Prompt promptBehavior = Prompt.LOGIN;
final String scopes = "user.read";
final String extraScopesToConsent = "";
final String claims = "";
final boolean enablePII = false;
final boolean forceRefresh = false;
final String authority = "";
final Constants.AuthScheme authScheme = Constants.AuthScheme.BEARER;
final String httpMethodTextFromSpinner = "NONE_NULL";
final HttpMethod popHttpMethod = null;
final String popResourceUrl = "";
return new RequestOptions(
configFile,
loginHint,
account,
promptBehavior,
scopes,
extraScopesToConsent,
claims,
enablePII,
forceRefresh,
authority,
authScheme,
popHttpMethod,
popResourceUrl
);
}
}
My Build.Gradle class:
apply plugin: 'com.android.library'
android { compileSdkVersion rootProject.ext.compileSdkVersion defaultConfig { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode 1 versionName "1.0" } signingConfigs { debug { storeFile file("./debug.keystore") storePassword 'android' keyAlias 'androiddebugkey' keyPassword 'android' }
release {
storeFile file("./debug.keystore")
storePassword 'android'
keyAlias 'androiddebugkey'
keyPassword 'android'
}
}
buildTypes {
release {
signingConfig signingConfigs.release
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
lintOptions {
abortOnError false
}
flavorDimensions "main"
productFlavors {
local {
//applicationIdSuffix ".local"
versionNameSuffix "-local"
resValue("string", "application_name", "msal-local")
}
dist {
// Keep .local because the redirect url we registered on the portal contains .local, not .dist
//applicationIdSuffix ".local"
versionNameSuffix "-dist"
resValue("string", "application_name", "msal-dist")
}
}
}
dependencies { // Compile Dependency localImplementation project(':msal') distImplementation 'com.microsoft.identity.client:msal:1.+' implementation "com.google.code.gson:gson:$rootProject.ext.gsonVersion" implementation "androidx.appcompat:appcompat:$rootProject.ext.appCompatVersion" implementation "androidx.legacy:legacy-support-v4:$rootProject.ext.legacySupportV4Version" implementation "com.google.android.material:material:$rootProject.ext.materialVersion" }
My unity class:
public void SignNative() {
//Llamamos a la actividad de android
AndroidJavaClass UnityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject context = UnityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
AndroidJavaObject tz_object = new AndroidJavaObject("com.iar.mfa.Autenticator");
string token = tz_object.Call<string>("Autenticate", context);
}
I would appreciate if someone could help me out. Thank you in advance
You have to include MSAL as a gradle dependency to your aar library project, and also include msal as a dependency in the gradle file that Unity accepts as input. Also there are a couple samples on github that demonstrate different approaches to getting android native plugins working in Android - and really the issue you're facing here is that - how to do Unity + Android native. Not MSAL.
Closing as external per @ashikns's comment
For people reading this in future, it's possible to use msal for android in Unity.
- Include msal as an aar plugin (or you can pull it as a gradle dependency using maintemplate.gradle support in Unity).
- Make an android library that calls msal and returns the access token as string. Unity supports calling Android classes and returning values from them: https://docs.unity3d.com/ScriptReference/AndroidJavaClass.html, https://docs.unity3d.com/ScriptReference/AndroidJavaProxy.html
For initializing msal you need a reference to the Unity activity. On C# side get Unity activity like this:
AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"); AndroidJavaObject unityActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
Pass this to java plugin and call
getApplicationContext()
to get the context needed to initialize msal.
Thanks this was helpful and we've successfully gotten this working in our Unity app
Hi, we are trying to implement MSAL in Unity. Have installed MSAL with Nuget in Unity application. Have configured redirect URL to "http"//localhost" and hae written the code. It's working in PlayMode (Able to authenticate the user and receiving the access token). But the problem is it's throwing error in apk run, getting below error:
It's not able to open a browser with commands like xdg-open and related
Any help will be much appreciated.
Using Msal in Unity for UI token authentication relies on objects that Unity itself doesn't generate, in the case of Android "Activities". It makes it pretty unfriendly to incorporate into projects.
Ideally a fully in house solution, such as automatically referencing the unity player as the current activity.
Simple clear and concise documentation of how to use Msal with Unity would be really helpful. It seems way more complicated than it needs too, and an almost total lack of documentation makes it incredibly difficult to track and understand issues.