1 //------------------------------------------------------------------------------
3 // Copyright (c) Microsoft Corporation.
4 // All rights reserved.
6 // This code is licensed under the MIT License.
8 // Permission is hereby granted, free of charge, to any person obtaining a copy
9 // of this software and associated documentation files(the "Software"), to deal
10 // in the Software without restriction, including without limitation the rights
11 // to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
12 // copies of the Software, and to permit persons to whom the Software is
13 // furnished to do so, subject to the following conditions :
15 // The above copyright notice and this permission notice shall be included in
16 // all copies or substantial portions of the Software.
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
21 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26 //------------------------------------------------------------------------------
29 using System.Collections.Generic;
30 using System.Threading;
31 using System.Threading.Tasks;
32 using Android.Accounts;
34 using Android.Content;
37 namespace Microsoft.IdentityModel.Clients.ActiveDirectory
39 [Android.Runtime.Preserve(AllMembers = true)]
40 class BrokerHelper : IBrokerHelper
42 private static SemaphoreSlim readyForResponse = null;
43 private static AuthenticationResultEx resultEx = null;
45 private BrokerProxy mBrokerProxy = new BrokerProxy(Application.Context);
47 public IPlatformParameters PlatformParameters { get; set; }
49 private bool WillUseBroker()
51 PlatformParameters pp = PlatformParameters as PlatformParameters;
60 public bool CanInvokeBroker { get { return WillUseBroker() && mBrokerProxy.CanSwitchToBroker(); } }
63 public async Task<AuthenticationResultEx> AcquireTokenUsingBroker(IDictionary<string, string> brokerPayload)
66 readyForResponse = new SemaphoreSlim(0);
69 await Task.Run(() => AcquireToken(brokerPayload)).ConfigureAwait(false);
73 PlatformPlugin.Logger.Error(null, exc);
76 await readyForResponse.WaitAsync().ConfigureAwait(false);
79 public void AcquireToken(IDictionary<string, string> brokerPayload)
82 if (brokerPayload.ContainsKey("broker_install_url"))
84 string url = brokerPayload["broker_install_url"];
85 Uri uri = new Uri(url);
86 string query = uri.Query;
87 if (query.StartsWith("?"))
89 query = query.Substring(1);
92 Dictionary<string, string> keyPair = EncodingHelper.ParseKeyValueList(query, '&', true, false, null);
94 PlatformParameters pp = PlatformParameters as PlatformParameters;
95 pp.CallerActivity.StartActivity(new Intent(Intent.ActionView, Android.Net.Uri.Parse(keyPair["app_link"])));
97 throw new AdalException(AdalErrorAndroidEx.BrokerApplicationRequired, AdalErrorMessageAndroidEx.BrokerApplicationRequired);
100 Context mContext = Application.Context;
101 AuthenticationRequest request = new AuthenticationRequest(brokerPayload);
102 PlatformParameters platformParams = PlatformParameters as PlatformParameters;
104 // BROKER flow intercepts here
105 // cache and refresh call happens through the authenticator service
106 if (mBrokerProxy.VerifyUser(request.LoginHint,
109 PlatformPlugin.Logger.Verbose(null, "It switched to broker for context: " + mContext.PackageName);
110 request.BrokerAccountName = request.LoginHint;
112 // Don't send background request, if prompt flag is always or
114 if (!string.IsNullOrEmpty(request.BrokerAccountName) || !string.IsNullOrEmpty(request.UserId))
116 PlatformPlugin.Logger.Verbose(null, "User is specified for background token request");
117 resultEx = mBrokerProxy.GetAuthTokenInBackground(request, platformParams.CallerActivity);
121 PlatformPlugin.Logger.Verbose(null, "User is not specified for background token request");
124 if (resultEx != null && resultEx.Result != null && !string.IsNullOrEmpty(resultEx.Result.AccessToken))
126 PlatformPlugin.Logger.Verbose(null, "Token is returned from background call ");
127 readyForResponse.Release();
131 // Launch broker activity
132 // if cache and refresh request is not handled.
133 // Initial request to authenticator needs to launch activity to
134 // record calling uid for the account. This happens for Prompt auto
135 // or always behavior.
136 PlatformPlugin.Logger.Verbose(null, "Token is not returned from backgroud call");
138 // Only happens with callback since silent call does not show UI
139 PlatformPlugin.Logger.Verbose(null, "Launch activity for Authenticator");
140 PlatformPlugin.Logger.Verbose(null, "Starting Authentication Activity");
141 if (resultEx == null)
143 PlatformPlugin.Logger.Verbose(null, "Initial request to authenticator");
144 // Log the initial request but not force a prompt
147 if (brokerPayload.ContainsKey("silent_broker_flow"))
149 throw new AdalSilentTokenAcquisitionException();
152 // onActivityResult will receive the response
153 // Activity needs to launch to record calling app for this
155 Intent brokerIntent = mBrokerProxy.GetIntentForBrokerActivity(request, platformParams.CallerActivity);
156 if (brokerIntent != null)
160 PlatformPlugin.Logger.Verbose(null, "Calling activity pid:" + Android.OS.Process.MyPid()
161 + " tid:" + Android.OS.Process.MyTid() + "uid:"
162 + Android.OS.Process.MyUid());
163 platformParams.CallerActivity.StartActivityForResult(brokerIntent, 1001);
165 catch (ActivityNotFoundException e)
167 PlatformPlugin.Logger.Error(null, e);
173 throw new AdalException(AdalErrorAndroidEx.NoBrokerAccountFound, "Add requested account as a Workplace account via Settings->Accounts or set UseBroker=true.");
177 internal static void SetBrokerResult(Intent data, int resultCode)
179 if (resultCode != BrokerResponseCode.ResponseReceived)
181 resultEx = new AuthenticationResultEx
184 new AdalException(data.GetStringExtra(BrokerConstants.ResponseErrorCode),
185 data.GetStringExtra(BrokerConstants.ResponseErrorMessage))
190 string accessToken = data.GetStringExtra(BrokerConstants.AccountAccessToken);
191 DateTimeOffset expiresOn = BrokerProxy.ConvertFromTimeT(data.GetLongExtra(BrokerConstants.AccountExpireDate, 0));
192 UserInfo userInfo = BrokerProxy.GetUserInfoFromBrokerResult(data.Extras);
193 resultEx = new AuthenticationResultEx
195 Result = new AuthenticationResult("Bearer", accessToken, expiresOn)
202 readyForResponse.Release();
206 internal class CallBackHandler : Java.Lang.Object, IAccountManagerCallback
208 public void Run(IAccountManagerFuture future)