-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathCustomAuthProvider.cs
107 lines (92 loc) · 5.35 KB
/
CustomAuthProvider.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System.Data.SqlClient;
// using Microsoft.Data.SqlClient; // Use MDS in future!
using System.Threading.Tasks;
using System.Threading;
using SampleSqlAuthProvider.Authentication;
namespace SampleSqlAuthProvider
{
/// <summary>
/// Provides an implementation of <see cref="SqlAuthenticationProvider"/> for SQL Tools to be able to perform Federated authentication
/// silently with Microsoft.Data.SqlClient integration only for "ActiveDirectory" authentication modes.
/// When registered, the SqlClient driver calls the <see cref="AcquireTokenAsync(SqlAuthenticationParameters)"/> API
/// with server-sent authority information to request access token when needed.
/// </summary>
public class CustomAuthProvider : SqlAuthenticationProvider
{
private const string s_defaultScopeSuffix = "/.default";
private readonly IAuthenticator authenticator;
/// <summary>
/// Instantiates AuthenticationProvider to be used for AAD authentication with MSAL.NET and MSAL.js co-ordinated.
/// </summary>
/// <param name="applicationName">Application Name that identifies user folder path location for reading/writing to shared cache.</param>
/// <param name="applicationPath">Application Path directory where application cache folder is present.</param>
/// <param name="authCallback">Callback that handles AAD authentication when user interaction is needed.</param>
public CustomAuthProvider(IAuthenticator authenticator)
{
this.authenticator = authenticator;
}
/// <summary>
/// Acquires access token with provided <paramref name="parameters"/>
/// </summary>
/// <param name="parameters">Authentication parameters</param>
/// <returns>Authentication token containing access token and expiry date.</returns>
public override async Task<SqlAuthenticationToken> AcquireTokenAsync(SqlAuthenticationParameters parameters)
{
// Setup scope
string resource = parameters.Resource;
string scope;
if (parameters.Resource.EndsWith(s_defaultScopeSuffix))
{
scope = parameters.Resource;
resource = parameters.Resource.Substring(0, parameters.Resource.LastIndexOf('/') + 1);
}
else
{
scope = parameters.Resource + s_defaultScopeSuffix;
}
string[] scopes = new string[] { scope };
CancellationTokenSource cts = new();
// With Microsoft.Data.SqlClient, we can use Connection timeout value to cancel token acquire request after certain period of time.
// cts.CancelAfter(parameters.ConnectionTimeout * 1000); // Convert to milliseconds
/* We split audience from Authority URL here. Audience can be one of the following:
* The Azure AD authority audience enumeration
* The tenant ID, which can be:
* - A GUID (the ID of your Azure AD instance), for single-tenant applications
* - A domain name associated with your Azure AD instance (also for single-tenant applications)
* One of these placeholders as a tenant ID in place of the Azure AD authority audience enumeration:
* - `organizations` for a multitenant application
* - `consumers` to sign in users only with their personal accounts
* - `common` to sign in users with their work and school accounts or their personal Microsoft accounts
*
* MSAL will throw a meaningful exception if you specify both the Azure AD authority audience and the tenant ID.
* If you don't specify an audience, your app will target Azure AD and personal Microsoft accounts as an audience. (That is, it will behave as though `common` were specified.)
* More information: https://docs.microsoft.com/azure/active-directory/develop/msal-client-application-configuration
**/
int seperatorIndex = parameters.Authority.LastIndexOf('/');
string authority = parameters.Authority.Remove(seperatorIndex + 1);
string audience = parameters.Authority.Substring(seperatorIndex + 1);
string userName = string.IsNullOrWhiteSpace(parameters.UserId) ? null : parameters.UserId;
AuthenticationParams @params = new(
AuthenticationMethod.ActiveDirectoryInteractive,
authority,
audience,
resource,
scopes,
userName!,
parameters.ConnectionId);
AccessToken result = await authenticator.GetTokenAsync(@params, cts.Token).ConfigureAwait(false);
return new SqlAuthenticationToken(result!.Token, result!.ExpiresOn);
}
/// <summary>
/// Whether or not provided <paramref name="authenticationMethod"/> is supported.
/// </summary>
/// <param name="authenticationMethod">SQL Authentication method</param>
/// <returns></returns>
public override bool IsSupported(SqlAuthenticationMethod authenticationMethod)
=> authenticationMethod == SqlAuthenticationMethod.ActiveDirectoryInteractive;
}
}