Polishing of C# auth API

pull/3756/head
Jan Tattermusch 9 years ago
parent 9f06aba55c
commit 18729a05e5
  1. 6
      src/csharp/Grpc.Auth/GoogleAuthInterceptors.cs
  2. 57
      src/csharp/Grpc.Auth/GoogleGrpcCredentials.cs
  3. 4
      src/csharp/Grpc.Auth/Grpc.Auth.csproj
  4. 4
      src/csharp/Grpc.Core.Tests/CallCredentialsTest.cs
  5. 6
      src/csharp/Grpc.Core.Tests/ChannelCredentialsTest.cs
  6. 30
      src/csharp/Grpc.Core/CallCredentials.cs
  7. 33
      src/csharp/Grpc.Core/ChannelCredentials.cs
  8. 8
      src/csharp/Grpc.IntegrationTesting/InteropClient.cs
  9. 2
      src/csharp/Grpc.IntegrationTesting/MetadataCredentialsTest.cs

@ -41,10 +41,10 @@ using Grpc.Core.Utils;
namespace Grpc.Auth namespace Grpc.Auth
{ {
/// <summary> /// <summary>
/// Factory methods to create authorization interceptors. /// Factory methods to create authorization interceptors for Google credentials.
/// <seealso cref="GrpcCredentials"/> /// <seealso cref="GoogleGrpcCredentials"/>
/// </summary> /// </summary>
public static class AuthInterceptors public static class GoogleAuthInterceptors
{ {
private const string AuthorizationHeader = "Authorization"; private const string AuthorizationHeader = "Authorization";
private const string Schema = "Bearer"; private const string Schema = "Bearer";

@ -33,6 +33,7 @@
using System; using System;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
using Google.Apis.Auth.OAuth2; using Google.Apis.Auth.OAuth2;
using Grpc.Core; using Grpc.Core;
@ -41,53 +42,55 @@ using Grpc.Core.Utils;
namespace Grpc.Auth namespace Grpc.Auth
{ {
/// <summary> /// <summary>
/// Factory methods to create instances of <see cref="ChannelCredentials"/> and <see cref="CallCredentials"/> classes. /// Factory/extension methods to create instances of <see cref="ChannelCredentials"/> and <see cref="CallCredentials"/> classes
/// based on credential objects originating from Google auth library.
/// </summary> /// </summary>
public static class GrpcCredentials public static class GoogleGrpcCredentials
{ {
/// <summary> /// <summary>
/// Creates a <see cref="MetadataCredentials"/> instance that will obtain access tokens /// Retrieves an instance of Google's Application Default Credentials using
/// from any credential that implements <c>ITokenAccess</c>. (e.g. <c>GoogleCredential</c>). /// <c>GoogleCredential.GetApplicationDefaultAsync()</c> and converts them
/// into a gRPC <see cref="ChannelCredentials"/> that use the default SSL credentials.
/// </summary> /// </summary>
/// <param name="credential">The credential to use to obtain access tokens.</param> /// <returns>The <c>ChannelCredentials</c> instance.</returns>
/// <returns>The <c>MetadataCredentials</c> instance.</returns> public static async Task<ChannelCredentials> GetApplicationDefaultAsync()
public static MetadataCredentials Create(ITokenAccess credential)
{ {
return new MetadataCredentials(AuthInterceptors.FromCredential(credential)); var googleCredential = await GoogleCredential.GetApplicationDefaultAsync().ConfigureAwait(false);
return googleCredential.ToChannelCredentials();
} }
/// <summary> /// <summary>
/// Convenience method to create a <see cref="ChannelCredentials"/> instance from /// Creates an instance of <see cref="CallCredentials"/> that will use given access token to authenticate
/// <c>ITokenAccess</c> credential and <c>SslCredentials</c> instance. /// with a gRPC service.
/// </summary> /// </summary>
/// <param name="credential">The credential to use to obtain access tokens.</param> /// <param name="accessToken">OAuth2 access token.</param>
/// <param name="sslCredentials">The <c>SslCredentials</c> instance.</param> /// /// <returns>The <c>MetadataCredentials</c> instance.</returns>
/// <returns>The channel credentials for access token based auth over a secure channel.</returns> public static CallCredentials FromAccessToken(string accessToken)
public static ChannelCredentials Create(ITokenAccess credential, SslCredentials sslCredentials)
{ {
return ChannelCredentials.Create(sslCredentials, Create(credential)); return CallCredentials.FromInterceptor(GoogleAuthInterceptors.FromAccessToken(accessToken));
} }
/// <summary> /// <summary>
/// Creates an instance of <see cref="MetadataCredentials"/> that will use given access token to authenticate /// Converts a <c>ITokenAccess</c> (e.g. <c>GoogleCredential</c>) object
/// with a gRPC service. /// into a gRPC <see cref="CallCredentials"/> object.
/// </summary> /// </summary>
/// <param name="accessToken">OAuth2 access token.</param> /// <param name="credential">The credential to use to obtain access tokens.</param>
/// /// <returns>The <c>MetadataCredentials</c> instance.</returns> /// <returns>The <c>CallCredentials</c> instance.</returns>
public static MetadataCredentials FromAccessToken(string accessToken) public static CallCredentials ToCallCredentials(this ITokenAccess credential)
{ {
return new MetadataCredentials(AuthInterceptors.FromAccessToken(accessToken)); return CallCredentials.FromInterceptor(GoogleAuthInterceptors.FromCredential(credential));
} }
/// <summary> /// <summary>
/// Converts a <c>ITokenAccess</c> object into a <see cref="MetadataCredentials"/> object supported /// Converts a <c>ITokenAccess</c> (e.g. <c>GoogleCredential</c>) object
/// by gRPC. /// into a gRPC <see cref="ChannelCredentials"/> object.
/// Default SSL credentials are used.
/// </summary> /// </summary>
/// <param name="credential"></param> /// <param name="googleCredential">The credential to use to obtain access tokens.</param>
/// <returns></returns> /// <returns>>The <c>ChannelCredentials</c> instance.</returns>
public static MetadataCredentials ToGrpcCredentials(this ITokenAccess credential) public static ChannelCredentials ToChannelCredentials(this ITokenAccess googleCredential)
{ {
return GrpcCredentials.Create(credential); return ChannelCredentials.Create(new SslCredentials(), googleCredential.ToCallCredentials());
} }
} }
} }

@ -78,9 +78,9 @@
<Compile Include="..\Grpc.Core\Version.cs"> <Compile Include="..\Grpc.Core\Version.cs">
<Link>Version.cs</Link> <Link>Version.cs</Link>
</Compile> </Compile>
<Compile Include="GrpcCredentials.cs" /> <Compile Include="GoogleGrpcCredentials.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="AuthInterceptors.cs" /> <Compile Include="GoogleAuthInterceptors.cs" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup> <ItemGroup>

@ -55,8 +55,8 @@ namespace Grpc.Core.Tests
public void CallCredentials_ToNativeCredentials() public void CallCredentials_ToNativeCredentials()
{ {
var composite = CallCredentials.Compose( var composite = CallCredentials.Compose(
new MetadataCredentials(async (uri, m) => { await Task.Delay(1); }), CallCredentials.FromInterceptor(async (uri, m) => { await Task.Delay(1); }),
new MetadataCredentials(async (uri, m) => { await Task.Delay(2); })); CallCredentials.FromInterceptor(async (uri, m) => { await Task.Delay(2); }));
using (var nativeComposite = composite.ToNativeCredentials()) using (var nativeComposite = composite.ToNativeCredentials())
{ {
} }

@ -63,11 +63,5 @@ namespace Grpc.Core.Tests
// forbid composing non-composable // forbid composing non-composable
Assert.Throws(typeof(ArgumentException), () => ChannelCredentials.Create(new FakeChannelCredentials(false), new FakeCallCredentials())); Assert.Throws(typeof(ArgumentException), () => ChannelCredentials.Create(new FakeChannelCredentials(false), new FakeCallCredentials()));
} }
[Test]
public void ChannelCredentials_CreateWrapped()
{
ChannelCredentials.Create(new FakeCallCredentials());
}
} }
} }

@ -40,6 +40,14 @@ using Grpc.Core.Utils;
namespace Grpc.Core namespace Grpc.Core
{ {
/// <summary>
/// Asynchronous authentication interceptor for <see cref="CallCredentials"/>.
/// </summary>
/// <param name="authUri">URL of a service to which current remote call needs to authenticate</param>
/// <param name="metadata">Metadata to populate with entries that will be added to outgoing call's headers.</param>
/// <returns></returns>
public delegate Task AsyncAuthInterceptor(string authUri, Metadata metadata);
/// <summary> /// <summary>
/// Client-side call credentials. Provide authorization with per-call granularity. /// Client-side call credentials. Provide authorization with per-call granularity.
/// </summary> /// </summary>
@ -56,6 +64,16 @@ namespace Grpc.Core
return new CompositeCallCredentials(credentials); return new CompositeCallCredentials(credentials);
} }
/// <summary>
/// Creates a new instance of <c>CallCredentials</c> class from an
/// interceptor that can attach metadata to outgoing calls.
/// </summary>
/// <param name="interceptor">authentication interceptor</param>
public static CallCredentials FromInterceptor(AsyncAuthInterceptor interceptor)
{
return new MetadataCredentials(interceptor);
}
/// <summary> /// <summary>
/// Creates native object for the credentials. /// Creates native object for the credentials.
/// </summary> /// </summary>
@ -63,19 +81,11 @@ namespace Grpc.Core
internal abstract CredentialsSafeHandle ToNativeCredentials(); internal abstract CredentialsSafeHandle ToNativeCredentials();
} }
/// <summary>
/// Asynchronous authentication interceptor for <see cref="MetadataCredentials"/>.
/// </summary>
/// <param name="authUri">URL of a service to which current remote call needs to authenticate</param>
/// <param name="metadata">Metadata to populate with entries that will be added to outgoing call's headers.</param>
/// <returns></returns>
public delegate Task AsyncAuthInterceptor(string authUri, Metadata metadata);
/// <summary> /// <summary>
/// Client-side credentials that delegate metadata based auth to an interceptor. /// Client-side credentials that delegate metadata based auth to an interceptor.
/// The interceptor is automatically invoked for each remote call that uses <c>MetadataCredentials.</c> /// The interceptor is automatically invoked for each remote call that uses <c>MetadataCredentials.</c>
/// </summary> /// </summary>
public class MetadataCredentials : CallCredentials internal sealed class MetadataCredentials : CallCredentials
{ {
readonly AsyncAuthInterceptor interceptor; readonly AsyncAuthInterceptor interceptor;
@ -85,7 +95,7 @@ namespace Grpc.Core
/// <param name="interceptor">authentication interceptor</param> /// <param name="interceptor">authentication interceptor</param>
public MetadataCredentials(AsyncAuthInterceptor interceptor) public MetadataCredentials(AsyncAuthInterceptor interceptor)
{ {
this.interceptor = interceptor; this.interceptor = Preconditions.CheckNotNull(interceptor);
} }
internal override CredentialsSafeHandle ToNativeCredentials() internal override CredentialsSafeHandle ToNativeCredentials()

@ -71,17 +71,6 @@ namespace Grpc.Core
return new CompositeChannelCredentials(channelCredentials, callCredentials); return new CompositeChannelCredentials(channelCredentials, callCredentials);
} }
/// <summary>
/// Creates a new instance of <c>ChannelCredentials</c> by wrapping
/// an instance of <c>CallCredentials</c>.
/// </summary>
/// <param name="callCredentials">Call credentials.</param>
/// <returns>The <c>ChannelCredentials</c> wrapping given call credentials.</returns>
public static ChannelCredentials Create(CallCredentials callCredentials)
{
return new WrappedCallCredentials(callCredentials);
}
/// <summary> /// <summary>
/// Creates native object for the credentials. May return null if insecure channel /// Creates native object for the credentials. May return null if insecure channel
/// should be created. /// should be created.
@ -213,26 +202,4 @@ namespace Grpc.Core
} }
} }
} }
/// <summary>
/// Credentials wrapping <see cref="CallCredentials"/> as <see cref="ChannelCredentials"/>.
/// </summary>
internal sealed class WrappedCallCredentials : ChannelCredentials
{
readonly CallCredentials callCredentials;
/// <summary>
/// Wraps instance of <c>CallCredentials</c> as <c>ChannelCredentials</c>.
/// </summary>
/// <param name="callCredentials">credentials to wrap</param>
public WrappedCallCredentials(CallCredentials callCredentials)
{
this.callCredentials = Preconditions.CheckNotNull(callCredentials);
}
internal override CredentialsSafeHandle ToNativeCredentials()
{
return callCredentials.ToNativeCredentials();
}
}
} }

@ -143,14 +143,14 @@ namespace Grpc.IntegrationTesting
{ {
var googleCredential = await GoogleCredential.GetApplicationDefaultAsync(); var googleCredential = await GoogleCredential.GetApplicationDefaultAsync();
Assert.IsTrue(googleCredential.IsCreateScopedRequired); Assert.IsTrue(googleCredential.IsCreateScopedRequired);
credentials = ChannelCredentials.Create(credentials, googleCredential.ToGrpcCredentials()); credentials = ChannelCredentials.Create(credentials, googleCredential.ToCallCredentials());
} }
if (options.TestCase == "compute_engine_creds") if (options.TestCase == "compute_engine_creds")
{ {
var googleCredential = await GoogleCredential.GetApplicationDefaultAsync(); var googleCredential = await GoogleCredential.GetApplicationDefaultAsync();
Assert.IsFalse(googleCredential.IsCreateScopedRequired); Assert.IsFalse(googleCredential.IsCreateScopedRequired);
credentials = ChannelCredentials.Create(credentials, googleCredential.ToGrpcCredentials()); credentials = ChannelCredentials.Create(credentials, googleCredential.ToCallCredentials());
} }
return credentials; return credentials;
} }
@ -392,7 +392,7 @@ namespace Grpc.IntegrationTesting
ITokenAccess credential = (await GoogleCredential.GetApplicationDefaultAsync()).CreateScoped(new[] { oauthScope }); ITokenAccess credential = (await GoogleCredential.GetApplicationDefaultAsync()).CreateScoped(new[] { oauthScope });
string oauth2Token = await credential.GetAccessTokenForRequestAsync(); string oauth2Token = await credential.GetAccessTokenForRequestAsync();
var credentials = GrpcCredentials.FromAccessToken(oauth2Token); var credentials = GoogleGrpcCredentials.FromAccessToken(oauth2Token);
var request = new SimpleRequest var request = new SimpleRequest
{ {
FillUsername = true, FillUsername = true,
@ -412,7 +412,7 @@ namespace Grpc.IntegrationTesting
Console.WriteLine("running per_rpc_creds"); Console.WriteLine("running per_rpc_creds");
ITokenAccess googleCredential = await GoogleCredential.GetApplicationDefaultAsync(); ITokenAccess googleCredential = await GoogleCredential.GetApplicationDefaultAsync();
var credentials = GrpcCredentials.Create(googleCredential); var credentials = googleCredential.ToCallCredentials();
var request = new SimpleRequest var request = new SimpleRequest
{ {
FillUsername = true, FillUsername = true,

@ -75,7 +75,7 @@ namespace Grpc.IntegrationTesting
var clientCredentials = ChannelCredentials.Create( var clientCredentials = ChannelCredentials.Create(
new SslCredentials(File.ReadAllText(TestCredentials.ClientCertAuthorityPath)), new SslCredentials(File.ReadAllText(TestCredentials.ClientCertAuthorityPath)),
new MetadataCredentials(asyncAuthInterceptor)); CallCredentials.FromInterceptor(asyncAuthInterceptor));
channel = new Channel(Host, server.Ports.Single().BoundPort, clientCredentials, options); channel = new Channel(Host, server.Ports.Single().BoundPort, clientCredentials, options);
client = TestService.NewClient(channel); client = TestService.NewClient(channel);
} }

Loading…
Cancel
Save