I made BasicAuth and WindowsAuth work in my SignalR project.
Now I am looking for other ways of authenticating (without needing a Win/AD Account).
While reading the SignalR documentation I stumbled upon the possibility to provide auth tokens in the connection header:
http://www.asp.net/signalr/overview/security/hub-authorization#header
It states "Then, in the hub, you would verify the user's token."
I could make the OnConnected method to be access anonymously and get the token like the following and then verifying it:
var test = Context.Request.Headers["mytoken"];
But what would would be the next step? I would need to set the connected user to be an authenticated user but how can I do that manually?
My overall goal is to have a very simple method of authentication,i.e. a "hardcoded" token validated on the server side and grant access to the other methods which have authorization enabled.
Any help would be appreciated.
I have had a similar problem. I found a kind of workaround creating a new AuthorizeAttribute. Next, I decorated the methods with this attribute. When a request is made, the attribute checks the token and gives the permission or not to be accessed.
Here is the code:
[AttributeUsage(AttributeTargets.Method)]
internal class CustomAuthorizeAttribute : AuthorizeAttribute
{
public override bool AuthorizeHubMethodInvocation(Microsoft.AspNet.SignalR.Hubs.IHubIncomingInvokerContext hubIncomingInvokerContext, bool appliesToMethod)
{
string token = hubIncomingInvokerContext.Hub.Context.Headers["AuthenticationToken"];
if (string.IsNullOrEmpty(token))
return false;
else
{
string decryptedValue = Encryptor.Decrypt(token, Encryptor.Password);
string[] values = decryptedValue.Split(';');
string userName = values[0],
deviceId = values[1],
connectionId = values[2];
bool b = ...CanAccess()...;
return b;
}
}
}
To have a username, you can simply add a property in your Hub that reads the token, parse it and returns the username.
Still can't use Context.User.Identity, though. I hope it helps.
Related
I implemented Google OAUTH used google API:
AuthorizationCodeMvcApp authorizationCodeMvcApp = new AuthorizationCodeMvcApp(controller, flowData);
AuthorizationCodeWebApp.AuthResult authorizationResult = await authorizationCodeMvcApp.AuthorizeAsync(CancellationToken.None);
with custom FlowMetadata where override:
public override string AuthCallback
{
get
{
string controllerName = nameof(BillsEmailsConfigurationController);
controllerName = SubstringController(controllerName);
string actionName = "Callback";
return $"/{controllerName}/{actionName}";
}
}
everything works fine but in the production Server, we have a load balancing where the local protocol is HTTP, and only outside we use HTTPS, how to force to put HTTPS prefix to callback for google OAuth API?
ps. return full URL like 'https://..../Callback' doesn't work
Found out the solution is to write custom AuthorizationCodeMvcApp that implemented from AuthorizationCodeWebApp here is the source https://github.com/googleapis/google-api-dotnet-client/blob/master/Src/Support/Google.Apis.Auth.Mvc/OAuth2/Mvc/AuthorizationCodeMvcApp.cs
and in line #46 you can see the passing of the full callback URL
We are trying to communicate with a REST server, which uses its own OAuth2 implementation.
This server is written by another company in Java, so I don't have much influence about it.
I've got all the necessary information, like Access Token URL, Refresh URL, Client Id, Client Secret, etc. I can already request an access token and then request some other data from this server, using the REST client Postman.
Now I'd like to use the ServiceStack client (version 4.5.14), to communicate with this server in C# .NET 4.6.2.
My problem is: All the examples I found, e.g. http://docs.servicestack.net/authentication-and-authorization#custom-authentication-and-authorization are either about the server-side or about authentication against Facebook or Google.
I already implemented my own CustomOAuth2Provider, setting the access token URL, ConsumerSecret, etc.
But how do I tell the JsonServiceClient, to use this Provider, before executing the specific request?
Thank you,
Daniel
Edit:
I read a lot of documentation and ServiceStack sourcecode, and I think my main problems are the following:
I abuse the ServiceStack Client to communicate with a non-ServiceStack application, which I can not modify.
Maybe the OAuth2 implementation of the third-party application is not 100% correct, as it expects authorization and token request in the same request.
But I got it working now and would like to show my solution here.
It still can be improved, e.g. it does not use the received refresh token right now.
public class ThirdPartyAuthenticator : IDisposable
{
// TODO: Move to config
public const string AccessTokenUrl = "";
public const string ConsumerKey = "";
public const string ConsumerSecret = "";
public const string Username = "";
public const string Password = "";
/// <summary>
/// Remember the last response, instance comprehensive so we do not need a new token for every request
/// </summary>
public static ServiceModel.ThirdPartyOAuth2Response LastOAuthResponse = null;
/// <summary>
/// This already authenticated client can be used for the data requests.
/// </summary>
public JsonServiceClient AuthenticatedServiceClient { get; set; }
public ThirdPartyAuthenticator()
{
if (LastOAuthResponse == null || (LastOAuthResponse.ExpiryDateTime < DateTime.Now)) // TODO: Use Refresh token?
{
// Get token first
JsonServiceClient authClient = new JsonServiceClient(AccessTokenUrl);
authClient.UserName = ConsumerKey;
authClient.Password = ConsumerSecret;
authClient.AlwaysSendBasicAuthHeader = true;
var request = new ServiceModel.ThirdPartyOAuth2Request();
request.Username = Username;
request.Password = Password;
// Use the Get URI, because server expects username + password as query parameter
LastOAuthResponse = authClient.Post<ServiceModel.ThirdPartyOAuth2Response>(request.ToGetUrl(), request);
}
// If no exception was thrown, we have a valid token here.
AuthenticatedServiceClient = new JsonServiceClient(AccessTokenUrl);
AuthenticatedServiceClient.BearerToken = LastOAuthResponse.AccessToken;
}
public void Dispose()
{
AuthenticatedServiceClient?.Dispose();
}
}
usage:
using (var foo = new ThirdPartyAuthenticator())
{
var response = foo.AuthenticatedServiceClient.Get(new ServiceModel.GetMyData() { SomeId = 10 });
}
OAuth providers require a browser to redirect to the OAuth provider site where Users are able to accept authentication with the App and any permissions it requires. Once the user accepts they're redirected back to your ServiceStack App where it will create an Authenticated User Session. The session id from the Authenticated User Session is what's configured on the ServiceStack client to establish authenticated requests.
Here are some Example Apps which use OAuth to Authenticate using a browser then capture the browser redirect to extract the session cookies and configure it on the C# Service Client where they're then able to make Authenticated requests:
https://github.com/ServiceStackApps/TechStacksAuth
https://github.com/ServiceStackApps/AndroidJavaChat
I'm currently creating a web application on which the user can login via his Google account. This works client side but I would also like to secure REST API calls. To do so, I send the "Google id token" with each request via the "Authorization" header. Now, I would like to verify in C# that the token passed is valid. I found that there is a .NET library to do so but I didn't find anywhere any clear documentation on how to simply validate the token.
Does anyone have some pointer for this?
My answer is the same as the answer above with a little bit more details.
using Google.Apis.Auth;
using Google.Apis.Auth.OAuth2;
GoogleJsonWebSignature.Payload payload = await GoogleJsonWebSignature.ValidateAsync(Token);
...
The payload object contains all the information that you need.
According to the "Verify the integrity of the ID token" documentation multiple things must be checked, for the id token to be valid, not just the signature.
One of those is whether "the ID token is equal to [...] your app's client IDs". Since we never give the client ID to GoogleJsonWebSignature.ValidateAsync(token) it seems we need to check it manually. I'm assuming it's really just checking the signature and we need to do all of the other checks manually.
My first shot at this:
bool valid = true;
try
{
GoogleJsonWebSignature.Payload payload = await GoogleJsonWebSignature.ValidateAsync(Token);
if (!payload.Audience.Equals("YOUR_CLIENT_ID_1234567890.apps.googleusercontent.com"))
valid = false;
if (!payload.Issuer.Equals("accounts.google.com") && !payload.Issuer.Equals("https://accounts.google.com"))
valid = false;
if (payload.ExpirationTimeSeconds == null)
valid = false;
else
{
DateTime now = DateTime.Now.ToUniversalTime();
DateTime expiration = DateTimeOffset.FromUnixTimeSeconds((long)payload.ExpirationTimeSeconds).DateTime;
if (now > expiration)
{
valid = false;
}
}
}
catch (InvalidJwtException e)
{
valid = false;
}
For future reference the following verifications are checked internally by the Google.Apis.Auth library and no extra validations are required (both passing settings or checking the payload):
bad jwt (null, empty, too long, missing signature)
wrong algorithm
invalid signature
invalid issuer
signature time without tolerance
The following however require input by the developer in order to be validated. They can be passed with GoogleJsonWebSignature.ValidationSettings:
audience
hosted domain
signature time with tolerance
Source: Google.Apis.Auth.Tests/GoogleJsonWebSignatureTests.cs
According to the docs, the token must be validated by verifying the signature with Google's public key. Also check the aus, iss and exp claims, and the hd claim if applies.
Therefore only the aus (and hd) have to be tested explicitly by the developer.
try
{
//...
var validationSettings = new GoogleJsonWebSignature.ValidationSettings
{
Audience = new string[] { "[google-signin-client_id].apps.googleusercontent.com" }
};
var payload = await GoogleJsonWebSignature.ValidateAsync(idToken, validationSettings);
//...
}
catch (InvalidJwtException ex)
{
//...
}
Yet another simplified answer (for .net 6):
Add this nuget package to your project:
https://www.nuget.org/packages/Google.Apis.Auth
Add using statement:
using Google.Apis.Auth;
Create this method in your controller:
[AllowAnonymous]
[HttpPost("verify")]
public async Task<ActionResult> Verify(){
string token = Request.Headers["Authorization"].ToString().Remove(0,7); //remove Bearer
var payload = await VerifyGoogleTokenId(token);
if (payload==null)
{
return BadRequest("Invalid token");
}
return Ok(payload); }
Create the VerifyGoogleTokenId function:
public async Task<GoogleJsonWebSignature.Payload> VerifyGoogleTokenId(string token){
try
{
// uncomment these lines if you want to add settings:
// var validationSettings = new GoogleJsonWebSignature.ValidationSettings
// {
// Audience = new string[] { "yourServerClientIdFromGoogleConsole.apps.googleusercontent.com" }
// };
// Add your settings and then get the payload
// GoogleJsonWebSignature.Payload payload = await GoogleJsonWebSignature.ValidateAsync(token, validationSettings);
// Or Get the payload without settings.
GoogleJsonWebSignature.Payload payload = await GoogleJsonWebSignature.ValidateAsync(token);
return payload;
}
catch (System.Exception)
{
Console.WriteLine("invalid google token");
}
return null;
}
Test the implementation by sending a post request to yourapi.com/verify. Dont forget the authorization header.
Say thanks with an up vote.
My code snippet below is supposed to return the list of beacons. When having Google API Console generate an API Key I have whitelisted my public IP address and associated with the api key. When the code calls ExecuteAsync() method, I keep receiving an exception with error code 403 (forbidden). What may have I done wrong and how to mitigate the issue?
public async void TestApiKey()
{
var apikey = "739479874ABCDEFGH123456"; //it's not the real key I'm using
var beaconServices = new ProximitybeaconService(new Google.Apis.Services.BaseClientService.Initializer
{
ApplicationName = "My Project",
ApiKey = apikey
});
var result = await beaconServices.Beacons.List().ExecuteAsync();
// Display the results.
if (result.Beacons != null)
{
foreach (var api in result.Beacons)
{
Console.WriteLine(api.BeaconName + " - " + api.Status);
}
}
}
You are using a Public API key. Public API keys only work with public data.
beacons.list Authenticate using an OAuth access token from a
signed-in user with viewer, Is owner or Can edit permissions.
Requires the following OAuth scope:
•https://www.googleapis.com/auth/userlocation.beacon.registry
The method you are trying to access is accessing private user data. You need to be authentication before you can use it. Switch to Oauth2 authentication. Setting it to public probably wont work because you cant to my knowledge supply a scope to a public api key.
I'm trying to use the Google+ API to access info for the authenticated user. I've copied some code from one of the samples, which works fine (below), however I'm having trouble making it work in a way I can reuse the token across app-launches.
I tried capturing the "RefreshToken" property and using provider.RefreshToken() (amongst other things) and always get a 400 Bad Request response.
Does anyone know how to make this work, or know where I can find some samples? The Google Code site doesn't seem to cover this :-(
class Program
{
private const string Scope = "https://www.googleapis.com/auth/plus.me";
static void Main(string[] args)
{
var provider = new NativeApplicationClient(GoogleAuthenticationServer.Description);
provider.ClientIdentifier = "BLAH";
provider.ClientSecret = "BLAH";
var auth = new OAuth2Authenticator<NativeApplicationClient>(provider, GetAuthentication);
var plus = new PlusService(auth);
plus.Key = "BLAH";
var me = plus.People.Get("me").Fetch();
Console.WriteLine(me.DisplayName);
}
private static IAuthorizationState GetAuthentication(NativeApplicationClient arg)
{
// Get the auth URL:
IAuthorizationState state = new AuthorizationState(new[] { Scope });
state.Callback = new Uri(NativeApplicationClient.OutOfBandCallbackUrl);
Uri authUri = arg.RequestUserAuthorization(state);
// Request authorization from the user (by opening a browser window):
Process.Start(authUri.ToString());
Console.Write(" Authorization Code: ");
string authCode = Console.ReadLine();
Console.WriteLine();
// Retrieve the access token by using the authorization code:
return arg.ProcessUserAuthorization(authCode, state);
}
}
Here is an example. Make sure you add a string setting called RefreshToken and reference System.Security or find another way to safely store the refresh token.
private static byte[] aditionalEntropy = { 1, 2, 3, 4, 5 };
private static IAuthorizationState GetAuthorization(NativeApplicationClient arg)
{
// Get the auth URL:
IAuthorizationState state = new AuthorizationState(new[] { PlusService.Scopes.PlusMe.GetStringValue() });
state.Callback = new Uri(NativeApplicationClient.OutOfBandCallbackUrl);
string refreshToken = LoadRefreshToken();
if (!String.IsNullOrWhiteSpace(refreshToken))
{
state.RefreshToken = refreshToken;
if (arg.RefreshToken(state))
return state;
}
Uri authUri = arg.RequestUserAuthorization(state);
// Request authorization from the user (by opening a browser window):
Process.Start(authUri.ToString());
Console.Write(" Authorization Code: ");
string authCode = Console.ReadLine();
Console.WriteLine();
// Retrieve the access token by using the authorization code:
var result = arg.ProcessUserAuthorization(authCode, state);
StoreRefreshToken(state);
return result;
}
private static string LoadRefreshToken()
{
return Encoding.Unicode.GetString(ProtectedData.Unprotect(Convert.FromBase64String(Properties.Settings.Default.RefreshToken), aditionalEntropy, DataProtectionScope.CurrentUser));
}
private static void StoreRefreshToken(IAuthorizationState state)
{
Properties.Settings.Default.RefreshToken = Convert.ToBase64String(ProtectedData.Protect(Encoding.Unicode.GetBytes(state.RefreshToken), aditionalEntropy, DataProtectionScope.CurrentUser));
Properties.Settings.Default.Save();
}
The general idea is as follows:
You redirect the user to Google's Authorization Endpoint.
You obtain a short-lived Authorization Code.
You immediately exchange the Authorization Code for a long-lived Access Token using Google's Token Endpoint. The Access Token comes with an expiry date and a Refresh Token.
You make requests to Google's API using the Access Token.
You can reuse the Access Token for as many requests as you like until it expires. Then you can use the Refresh Token to request a new Access Token (which comes with a new expiry date and a new Refresh Token).
See also:
The OAuth 2.0 Authorization Protocol
Google's OAuth 2.0 documentation
I also had problems with getting "offline" authentication to work (i.e. acquiring authentication with a refresh token), and got HTTP-response 400 Bad request with a code similar to the OP's code. However, I got it to work with the line client.ClientCredentialApplicator = ClientCredentialApplicator.PostParameter(this.clientSecret); in the Authenticate-method. This is essential to get a working code -- I think this line forces the clientSecret to be sent as a POST-parameter to the server (instead of as a HTTP Basic Auth-parameter).
This solution assumes that you've already got a client ID, a client secret and a refresh-token. Note that you don't need to enter an access-token in the code. (A short-lived access-code is acquired "under the hood" from the Google server when sending the long-lived refresh-token with the line client.RefreshAuthorization(state);. This access-token is stored as part of the auth-variable, from where it is used to authorize the API-calls "under the hood".)
A code example that works for me with Google API v3 for accessing my Google Calendar:
class SomeClass
{
private string clientID = "XXXXXXXXX.apps.googleusercontent.com";
private string clientSecret = "MY_CLIENT_SECRET";
private string refreshToken = "MY_REFRESH_TOKEN";
private string primaryCal = "MY_GMAIL_ADDRESS";
private void button2_Click_1(object sender, EventArgs e)
{
try
{
NativeApplicationClient client = new NativeApplicationClient(GoogleAuthenticationServer.Description, this.clientID, this.clientSecret);
OAuth2Authenticator<NativeApplicationClient> auth = new OAuth2Authenticator<NativeApplicationClient>(client, Authenticate);
// Authenticated and ready for API calls...
// EITHER Calendar API calls (tested):
CalendarService cal = new CalendarService(auth);
EventsResource.ListRequest listrequest = cal.Events.List(this.primaryCal);
Google.Apis.Calendar.v3.Data.Events events = listrequest.Fetch();
// iterate the events and show them here.
// OR Plus API calls (not tested) - copied from OP's code:
var plus = new PlusService(auth);
plus.Key = "BLAH"; // don't know what this line does.
var me = plus.People.Get("me").Fetch();
Console.WriteLine(me.DisplayName);
// OR some other API calls...
}
catch (Exception ex)
{
Console.WriteLine("Error while communicating with Google servers. Try again(?). The error was:\r\n" + ex.Message + "\r\n\r\nInner exception:\r\n" + ex.InnerException.Message);
}
}
private IAuthorizationState Authenticate(NativeApplicationClient client)
{
IAuthorizationState state = new AuthorizationState(new string[] { }) { RefreshToken = this.refreshToken };
// IMPORTANT - does not work without:
client.ClientCredentialApplicator = ClientCredentialApplicator.PostParameter(this.clientSecret);
client.RefreshAuthorization(state);
return state;
}
}
The OAuth 2.0 spec is not yet finished, and there is a smattering of spec implementations out there across the various clients and services that cause these errors to appear. Mostly likely you're doing everything right, but the DotNetOpenAuth version you're using implements a different draft of OAuth 2.0 than Google is currently implementing. Neither part is "right", since the spec isn't yet finalized, but it makes compatibility something of a nightmare.
You can check that the DotNetOpenAuth version you're using is the latest (in case that helps, which it might), but ultimately you may need to either sit tight until the specs are finalized and everyone implements them correctly, or read the Google docs yourself (which presumably describe their version of OAuth 2.0) and implement one that specifically targets their draft version.
I would recommend looking at the "SampleHelper" project in the Samples solution of the Google .NET Client API:
Samples/SampleHelper/AuthorizationMgr.cs
This file shows both how to use Windows Protected Data to store a Refresh token, and it also shows how to use a Local Loopback Server and different techniques to capture the Access code instead of having the user enter it manually.
One of the samples in the library which use this method of authorization can be found below:
Samples/Tasks.CreateTasks/Program.cs