Skip to content

Commit 89fd694

Browse files
author
Hovsep
committed
Merge pull request Azure#1431 from hovsepm/clu
[#107739338] Implemented device code authentication.
2 parents 029a38e + 3088a92 commit 89fd694

File tree

27 files changed

+239
-163
lines changed

27 files changed

+239
-163
lines changed

src/CLU/Commands.Common.Authentication/Authentication/AdalTokenProvider.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,17 @@ public AdalTokenProvider()
3535

3636
public IAccessToken GetAccessToken(
3737
AdalConfiguration config,
38-
ShowDialog promptBehavior,
38+
AuthenticationBehavior behavior,
3939
string userId,
4040
string password,
4141
AzureAccount.AccountType credentialType)
4242
{
4343
switch (credentialType)
4444
{
4545
case AzureAccount.AccountType.User:
46-
return userTokenProvider.GetAccessToken(config, promptBehavior, userId, password, credentialType);
46+
return userTokenProvider.GetAccessToken(config, behavior, userId, password, credentialType);
4747
case AzureAccount.AccountType.ServicePrincipal:
48-
return servicePrincipalTokenProvider.GetAccessToken(config, promptBehavior, userId, password, credentialType);
48+
return servicePrincipalTokenProvider.GetAccessToken(config, behavior, userId, password, credentialType);
4949
default:
5050
throw new ArgumentException(Resources.UnknownCredentialType, "credentialType");
5151
}

src/CLU/Commands.Common.Authentication/Authentication/ShowDialog.cs renamed to src/CLU/Commands.Common.Authentication/Authentication/AuthenticationBehavior.cs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,21 @@
1212
// limitations under the License.
1313
// ----------------------------------------------------------------------------------
1414

15+
using Microsoft.IdentityModel.Clients.ActiveDirectory;
16+
using System;
17+
1518
namespace Microsoft.Azure.Commands.Common.Authentication
1619
{
17-
public enum ShowDialog
20+
public enum AuthenticationType
1821
{
19-
Auto,
20-
Always,
21-
Never
22+
DeviceCode,
23+
Silent
24+
}
25+
26+
public class AuthenticationBehavior
27+
{
28+
public Func<DeviceCodeResult, bool> DeviceCodeHandler { get; set; }
29+
30+
public AuthenticationType Type { get; set; }
2231
}
2332
}

src/CLU/Commands.Common.Authentication/Authentication/ITokenProvider.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,14 @@ public interface ITokenProvider
2828
/// and credential type.
2929
/// </summary>
3030
/// <param name="config">Configuration.</param>
31-
/// <param name="promptBehavior">Prompt behavior.</param>
31+
/// <param name="behavior">Prompt behavior.</param>
3232
/// <param name="userId">User ID/Service principal to get the token for.</param>
3333
/// <param name="password">Secure strings with password/service principal key.</param>
3434
/// <param name="credentialType">Credential type.</param>
3535
/// <returns>An access token.</returns>
3636
IAccessToken GetAccessToken(
3737
AdalConfiguration config,
38-
ShowDialog promptBehavior,
38+
AuthenticationBehavior behavior,
3939
string userId,
4040
string password,
4141
AzureAccount.AccountType credentialType);

src/CLU/Commands.Common.Authentication/Authentication/KeyStoreApplicationCredentialProvider.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,6 @@ public KeyStoreApplicationCredentialProvider(string tenant, string password)
4646
/// <returns></returns>
4747
public async Task<AuthenticationResult> AuthenticateAsync(string clientId, string audience, AuthenticationContext context)
4848
{
49-
50-
5149
return await context.AcquireTokenAsync(audience, new ClientCredential(clientId, _password));
5250
}
5351
}

src/CLU/Commands.Common.Authentication/Authentication/ServicePrincipalTokenProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public ServicePrincipalTokenProvider(Func<String, byte[]> certificateFinder)
3737
{
3838
_getCertificate = certificateFinder;
3939
}
40-
public IAccessToken GetAccessToken(AdalConfiguration config, ShowDialog promptBehavior, string userId, string password,
40+
public IAccessToken GetAccessToken(AdalConfiguration config, AuthenticationBehavior behavior, string userId, string password,
4141
AzureAccount.AccountType credentialType)
4242
{
4343
if (credentialType == AzureAccount.AccountType.User)

src/CLU/Commands.Common.Authentication/Authentication/UserTokenProvider.cs

Lines changed: 56 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
using System.Runtime.InteropServices;
2020
using Commands.Common.Authentication.Properties;
2121
using Microsoft.Rest.Azure.Authentication;
22+
using System.Management.Automation;
2223

2324
namespace Microsoft.Azure.Commands.Common.Authentication
2425
{
@@ -29,10 +30,9 @@ namespace Microsoft.Azure.Commands.Common.Authentication
2930
/// </summary>
3031
internal class UserTokenProvider : ITokenProvider
3132
{
32-
3333
public IAccessToken GetAccessToken(
3434
AdalConfiguration config,
35-
ShowDialog promptBehavior,
35+
AuthenticationBehavior behavior,
3636
string userId,
3737
string password,
3838
AzureAccount.AccountType credentialType)
@@ -42,7 +42,7 @@ public IAccessToken GetAccessToken(
4242
throw new ArgumentException(string.Format(Resources.InvalidCredentialType, "User"), "credentialType");
4343
}
4444

45-
return new AdalAccessToken(AcquireToken(config, promptBehavior, userId, password), this, config);
45+
return new AdalAccessToken(AcquireToken(config, behavior, userId, password), this, config);
4646
}
4747

4848
private readonly static TimeSpan expirationThreshold = TimeSpan.FromMinutes(5);
@@ -81,7 +81,7 @@ private void Renew(AdalAccessToken token)
8181
if (IsExpired(token))
8282
{
8383
ServiceClientTracing.Information(Resources.UPNExpiredTokenTrace);
84-
AuthenticationResult result = AcquireToken(token.Configuration, ShowDialog.Never, token.UserId, null);
84+
AuthenticationResult result = AcquireToken(token.Configuration, new AuthenticationBehavior { Type = AuthenticationType.Silent }, token.UserId, null);
8585

8686
if (result == null)
8787
{
@@ -104,7 +104,7 @@ private AuthenticationContext CreateContext(AdalConfiguration config)
104104

105105
// We have to run this in a separate thread to guarantee that it's STA. This method
106106
// handles the threading details.
107-
private AuthenticationResult AcquireToken(AdalConfiguration config, ShowDialog promptBehavior, string userId,
107+
private AuthenticationResult AcquireToken(AdalConfiguration config, AuthenticationBehavior behavior, string userId,
108108
string password)
109109
{
110110
AuthenticationResult result = null;
@@ -121,31 +121,64 @@ private AuthenticationResult AcquireToken(AdalConfiguration config, ShowDialog p
121121
config.AdEndpoint,
122122
config.ClientId,
123123
config.ClientRedirectUri);
124-
if (promptBehavior != ShowDialog.Never)
124+
if (behavior.Type == AuthenticationType.DeviceCode)
125125
{
126-
throw new NotImplementedException("Device based authencation is not implemented.");
127-
}
126+
if(behavior.DeviceCodeHandler == null)
127+
{
128+
throw new PSArgumentException("deviceCodeHandler");
129+
}
128130

129-
try
130-
{
131-
UserCredential credential = new UserCredential(userId, password);
132-
result = context.AcquireTokenAsync(
133-
config.ResourceClientUri,
134-
config.ClientId,
135-
credential)
136-
.ConfigureAwait(false).GetAwaiter().GetResult();
137-
return result;
131+
try
132+
{
133+
DeviceCodeResult codeResult = context.AcquireDeviceCodeAsync(
134+
config.ResourceClientUri,
135+
config.ClientId)
136+
.ConfigureAwait(false).GetAwaiter().GetResult();
137+
138+
if (behavior.DeviceCodeHandler(codeResult))
139+
{
140+
return context.AcquireTokenByDeviceCodeAsync(codeResult)
141+
.ConfigureAwait(false).GetAwaiter().GetResult();
142+
}
143+
144+
throw new AadAuthenticationCanceledException("Authentication cancelled.", null);
145+
}
146+
catch (AdalException adalException)
147+
{
148+
if (adalException.ErrorCode == AdalError.AuthenticationCanceled)
149+
{
150+
throw new AadAuthenticationCanceledException(adalException.Message, adalException);
151+
}
152+
153+
throw new AadAuthenticationFailedException(adalException.Message, adalException);
154+
}
138155
}
139-
catch (AdalException adalException)
156+
else if(behavior.Type == AuthenticationType.Silent)
140157
{
141-
if (adalException.ErrorCode == AdalError.AuthenticationCanceled)
158+
try
142159
{
143-
throw new AadAuthenticationCanceledException(adalException.Message, adalException);
160+
UserCredential credential = new UserCredential(userId, password);
161+
return context.AcquireTokenAsync(
162+
config.ResourceClientUri,
163+
config.ClientId,
164+
credential)
165+
.ConfigureAwait(false).GetAwaiter().GetResult();
144166
}
167+
catch (AdalException adalException)
168+
{
169+
if (adalException.ErrorCode == AdalError.AuthenticationCanceled)
170+
{
171+
throw new AadAuthenticationCanceledException(adalException.Message, adalException);
172+
}
145173

146-
throw new AadAuthenticationFailedException(adalException.Message, adalException);
174+
throw new AadAuthenticationFailedException(adalException.Message, adalException);
175+
}
176+
}
177+
else
178+
{
179+
throw new NotImplementedException(
180+
string.Format("'{0}' authentication is not implemented.", behavior.Type));
147181
}
148-
149182
}
150183

151184
/// <summary>
@@ -187,8 +220,7 @@ public LoginType LoginType
187220
}
188221
}
189222
}
190-
191-
223+
192224
private void ClearCookies()
193225
{
194226
NativeMethods.InternetSetOption(IntPtr.Zero, NativeMethods.INTERNET_OPTION_END_BROWSER_SESSION, IntPtr.Zero, 0);

src/CLU/Commands.Common.Authentication/AuthenticationFactory.cs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ public IAccessToken Authenticate(
4646
AzureAccount account,
4747
AzureEnvironment environment,
4848
string tenant,
49-
string password,
49+
string password,
50+
AuthenticationBehavior behavior,
5051
TokenCache tokenCache,
5152
AzureEnvironment.Endpoint resourceId = AzureEnvironment.Endpoint.ActiveDirectoryServiceEndpointResourceId)
5253
{
@@ -64,8 +65,7 @@ public IAccessToken Authenticate(
6465
}
6566
else
6667
{
67-
68-
token = TokenProvider.GetAccessToken(configuration, ShowDialog.Never, account.Id, password, account.Type);
68+
token = TokenProvider.GetAccessToken(configuration, behavior, account.Id, password, account.Type);
6969
}
7070

7171
account.Id = token.UserId;
@@ -79,7 +79,14 @@ public IAccessToken Authenticate(
7979
string password,
8080
AzureEnvironment.Endpoint resourceId = AzureEnvironment.Endpoint.ActiveDirectoryServiceEndpointResourceId)
8181
{
82-
return Authenticate(account, environment, tenant, password, TokenCache.DefaultShared, resourceId);
82+
return Authenticate(
83+
account,
84+
environment,
85+
tenant,
86+
password,
87+
new AuthenticationBehavior { Type = AuthenticationType.Silent },
88+
TokenCache.DefaultShared,
89+
resourceId);
8390
}
8491

8592
public ServiceClientCredentials GetSubscriptionCloudCredentials(AzureContext context)
@@ -151,8 +158,12 @@ public ServiceClientCredentials GetSubscriptionCloudCredentials(
151158
tokenCache = new TokenCache(context.TokenCache);
152159
}
153160

154-
var token = Authenticate(context.Account, context.Environment,
155-
tenant, null, tokenCache);
161+
var token = Authenticate(
162+
context.Account,
163+
context.Environment,
164+
tenant, null,
165+
new AuthenticationBehavior { Type = AuthenticationType.Silent },
166+
tokenCache);
156167

157168
if (context.TokenCache != null && context.TokenCache.Length > 0)
158169
{

src/CLU/Commands.Common.Authentication/IAuthenticationFactory.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,16 @@ public interface IAuthenticationFactory
2828
/// <param name="environment">The azure environment object</param>
2929
/// <param name="tenant">The AD tenant in most cases should be 'common'</param>
3030
/// <param name="password">The AD account password</param>
31+
/// <param name="behavior">The authentication behavior</param>
3132
/// <param name="tokenCache">Token Cache</param>
3233
/// <param name="resourceId">Optional, the AD resource id</param>
3334
/// <returns></returns>
3435
IAccessToken Authenticate(
3536
AzureAccount account,
3637
AzureEnvironment environment,
3738
string tenant,
38-
string password,
39+
string password,
40+
AuthenticationBehavior behavior,
3941
TokenCache tokenCache,
4042
AzureEnvironment.Endpoint resourceId = AzureEnvironment.Endpoint.ActiveDirectoryServiceEndpointResourceId);
4143

@@ -56,6 +58,7 @@ IAccessToken Authenticate(
5658
AzureEnvironment.Endpoint resourceId = AzureEnvironment.Endpoint.ActiveDirectoryServiceEndpointResourceId);
5759

5860
ServiceClientCredentials GetSubscriptionCloudCredentials(AzureContext context);
61+
5962
ServiceClientCredentials GetSubscriptionCloudCredentials(AzureContext context, AzureEnvironment.Endpoint targetEndpoint);
6063

6164
ServiceClientCredentials GetServiceClientCredentials(AzureContext context);

src/CLU/Commands.Common.Authentication/project.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"System.Linq": "4.0.1-beta-23516",
1919
"Microsoft.CLU": "1.0.0",
2020
"Microsoft.IdentityModel.Clients.ActiveDirectory": "3.6.210231457-alpha",
21-
"Microsoft.Rest.ClientRuntime": "1.4.1",
21+
"Microsoft.Rest.ClientRuntime": "1.5.0",
2222
"Microsoft.Rest.ClientRuntime.Azure.Authentication": "1.0.0-preview",
2323
"Newtonsoft.Json": "7.0.1",
2424
"System.Collections": "4.0.11-beta-23516",

src/CLU/Commands.Common.Storage/project.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"Commands.Common": "",
2121
"Commands.Common.Authentication": "",
2222
"Microsoft.IdentityModel.Clients.ActiveDirectory": "3.6.210231457-alpha",
23-
"Microsoft.Rest.ClientRuntime": "1.4.1",
23+
"Microsoft.Rest.ClientRuntime": "1.5.0",
2424
"Newtonsoft.Json": "7.0.1",
2525
"System.Collections": "4.0.11-beta-23516",
2626
"System.Collections.Concurrent": "4.0.11-beta-23516",

src/CLU/Commands.Common/AzurePSCmdlet.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,12 +166,12 @@ protected static void InitializeDataCollectionProfile()
166166
{
167167
if (string.Equals(value, bool.FalseString, StringComparison.OrdinalIgnoreCase))
168168
{
169-
// Disable data collection only if it is explictly set to 'false'.
169+
// Disable data collection only if it is explicitly set to 'false'.
170170
_dataCollectionProfile = new AzurePSDataCollectionProfile(true);
171171
}
172172
else if (string.Equals(value, bool.TrueString, StringComparison.OrdinalIgnoreCase))
173173
{
174-
// Enable data collection only if it is explictly set to 'true'.
174+
// Enable data collection only if it is explicitly set to 'true'.
175175
_dataCollectionProfile = new AzurePSDataCollectionProfile(false);
176176
}
177177
}

src/CLU/Commands.Common/project.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"Microsoft.CLU": "1.0.0",
2020
"Commands.Common.Authentication": "",
2121
"Microsoft.IdentityModel.Clients.ActiveDirectory": "3.6.210231457-alpha",
22-
"Microsoft.Rest.ClientRuntime": "1.4.1",
22+
"Microsoft.Rest.ClientRuntime": "1.5.0",
2323
"Newtonsoft.Json": "7.0.1",
2424
"System.Collections": "4.0.11-beta-23516",
2525
"System.Collections.Concurrent": "4.0.11-beta-23516",

src/CLU/Commands.ResourceManager.Cmdlets/project.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
"Commands.Common": "",
2222
"Commands.ResourceManager.Common": "",
2323
"Microsoft.IdentityModel.Clients.ActiveDirectory": "3.6.210231457-alpha",
24-
"Microsoft.Rest.ClientRuntime": "1.4.1",
24+
"Microsoft.Rest.ClientRuntime": "1.5.0",
2525
"Newtonsoft.Json": "7.0.1",
2626
"System.Collections": "4.0.11-beta-23516",
2727
"System.Collections.Concurrent": "4.0.11-beta-23516",

src/CLU/Commands.ResourceManager.Common/project.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"Commands.Common": "",
2121
"Commands.Common.Authentication": "",
2222
"Microsoft.IdentityModel.Clients.ActiveDirectory": "3.6.210231457-alpha",
23-
"Microsoft.Rest.ClientRuntime": "1.4.1",
23+
"Microsoft.Rest.ClientRuntime": "1.5.0",
2424
"Newtonsoft.Json": "7.0.1",
2525
"System.Collections": "4.0.11-beta-23516",
2626
"System.Collections.Concurrent": "4.0.11-beta-23516",

src/CLU/Commands.ScenarioTests.ResourceManager.Common/Mocks/MockAccessTokenProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public MockAccessTokenProvider(string token, string userId)
3636
};
3737
}
3838

39-
public IAccessToken GetAccessToken(AdalConfiguration config, ShowDialog promptBehavior, string userId, string password,
39+
public IAccessToken GetAccessToken(AdalConfiguration config, AuthenticationBehavior behavior, string userId, string password,
4040
AzureAccount.AccountType credentialType)
4141
{
4242
return this.accessToken;

src/CLU/Commands.ScenarioTests.ResourceManager.Common/Mocks/MockCertificateAuthenticationFactory.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public IAccessToken Authenticate(
4040
AzureEnvironment environment,
4141
string tenant,
4242
string password,
43+
AuthenticationBehavior behavior,
4344
IdentityModel.Clients.ActiveDirectory.TokenCache tokenCache,
4445
AzureEnvironment.Endpoint resourceId = AzureEnvironment.Endpoint.ActiveDirectoryServiceEndpointResourceId)
4546
{
@@ -65,7 +66,14 @@ public IAccessToken Authenticate(
6566
string password,
6667
AzureEnvironment.Endpoint resourceId = AzureEnvironment.Endpoint.ActiveDirectoryServiceEndpointResourceId)
6768
{
68-
return Authenticate(account, environment, tenant, password, TokenCache.DefaultShared, resourceId);
69+
return Authenticate(
70+
account,
71+
environment,
72+
tenant,
73+
password,
74+
new AuthenticationBehavior { Type = AuthenticationType.Silent },
75+
TokenCache.DefaultShared,
76+
resourceId);
6977
}
7078

7179

0 commit comments

Comments
 (0)