Merge pull request #3756 from jtattermusch/adjust_csharp_auth_api

Improving the C# auth API
pull/3816/head
Jan Tattermusch 9 years ago
commit d86742f961
  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
{
/// <summary>
/// Factory methods to create authorization interceptors.
/// <seealso cref="GrpcCredentials"/>
/// Factory methods to create authorization interceptors for Google credentials.
/// <seealso cref="GoogleGrpcCredentials"/>
/// </summary>
public static class AuthInterceptors
public static class GoogleAuthInterceptors
{
private const string AuthorizationHeader = "Authorization";
private const string Schema = "Bearer";

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

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

@ -63,11 +63,5 @@ namespace Grpc.Core.Tests
// forbid composing non-composable
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
{
/// <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>
/// Client-side call credentials. Provide authorization with per-call granularity.
/// </summary>
@ -56,6 +64,16 @@ namespace Grpc.Core
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>
/// Creates native object for the credentials.
/// </summary>
@ -63,19 +81,11 @@ namespace Grpc.Core
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>
/// 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>
/// </summary>
public class MetadataCredentials : CallCredentials
internal sealed class MetadataCredentials : CallCredentials
{
readonly AsyncAuthInterceptor interceptor;
@ -85,7 +95,7 @@ namespace Grpc.Core
/// <param name="interceptor">authentication interceptor</param>
public MetadataCredentials(AsyncAuthInterceptor interceptor)
{
this.interceptor = interceptor;
this.interceptor = Preconditions.CheckNotNull(interceptor);
}
internal override CredentialsSafeHandle ToNativeCredentials()

@ -71,17 +71,6 @@ namespace Grpc.Core
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>
/// Creates native object for the credentials. May return null if insecure channel
/// 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();
Assert.IsTrue(googleCredential.IsCreateScopedRequired);
credentials = ChannelCredentials.Create(credentials, googleCredential.ToGrpcCredentials());
credentials = ChannelCredentials.Create(credentials, googleCredential.ToCallCredentials());
}
if (options.TestCase == "compute_engine_creds")
{
var googleCredential = await GoogleCredential.GetApplicationDefaultAsync();
Assert.IsFalse(googleCredential.IsCreateScopedRequired);
credentials = ChannelCredentials.Create(credentials, googleCredential.ToGrpcCredentials());
credentials = ChannelCredentials.Create(credentials, googleCredential.ToCallCredentials());
}
return credentials;
}
@ -392,7 +392,7 @@ namespace Grpc.IntegrationTesting
ITokenAccess credential = (await GoogleCredential.GetApplicationDefaultAsync()).CreateScoped(new[] { oauthScope });
string oauth2Token = await credential.GetAccessTokenForRequestAsync();
var credentials = GrpcCredentials.FromAccessToken(oauth2Token);
var credentials = GoogleGrpcCredentials.FromAccessToken(oauth2Token);
var request = new SimpleRequest
{
FillUsername = true,
@ -412,7 +412,7 @@ namespace Grpc.IntegrationTesting
Console.WriteLine("running per_rpc_creds");
ITokenAccess googleCredential = await GoogleCredential.GetApplicationDefaultAsync();
var credentials = GrpcCredentials.Create(googleCredential);
var credentials = googleCredential.ToCallCredentials();
var request = new SimpleRequest
{
FillUsername = true,

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

Loading…
Cancel
Save