Exchanging a google idToken for local openId token c# - c#

I am using this github project https://github.com/openiddict/openiddict-core which is great. But I am stuck as to what the procedures should be, or how to implement them, when the user uses an external identity provider, for this example, I will use google.
I have an angular2 app running, with an aspnet core webAPI. All my local logins work perfectly, I call connect/token with a username and password, and an accessToken is returned.
Now I need to implement google as an external identity provider. I have followed all the steps here to implement a google login button. This opens a popup when the user logins in. This is the code I have created for my google button.
// Angular hook that allows for interaction with elements inserted by the
// rendering of a view.
ngAfterViewInit() {
// check if the google client id is in the pages meta tags
if (document.querySelector("meta[name='google-signin-client_id']")) {
// Converts the Google login button stub to an actual button.
gapi.signin2.render(
'google-login-button',
{
"onSuccess": this.onGoogleLoginSuccess,
"scope": "profile",
"theme": "dark"
});
}
}
onGoogleLoginSuccess(loggedInUser) {
let idToken = loggedInUser.getAuthResponse().id_token;
// here i can pass the idToken up to my server and validate it
}
Now I have an idToken from google. The next step on the google pages found here says that I need to validate the google accessToken, which I can do, but how do I exchange the accessToken that I have from google, and create local accessToken which can be used on my application?

Edit: this answer was updated to use OpenIddict 3.x.
The next step on the google pages found here says that i need to validate the google accessToken, which i can do, but how do i exchange the accessToken that i have from google, and create local accessToken which can be used on my application?
The flow you're trying to implement is known as assertion grant. You can read this other SO post for more information about it.
OpenIddict fully supports custom grants, so this is something you can easily implement in your token endpoint action:
[HttpPost("~/connect/token"), Produces("application/json")]
public IActionResult Exchange()
{
var request = HttpContext.GetOpenIddictServerRequest();
if (request.GrantType == "urn:ietf:params:oauth:grant-type:google_identity_token")
{
// Reject the request if the "assertion" parameter is missing.
if (string.IsNullOrEmpty(request.Assertion))
{
return Forbid(
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidRequest,
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] =
"The mandatory 'assertion' parameter was missing."
}));
}
// Create a new ClaimsIdentity containing the claims that
// will be used to create an id_token and/or an access token.
var identity = new ClaimsIdentity(TokenValidationParameters.DefaultAuthenticationType);
// Manually validate the identity token issued by Google, including the
// issuer, the signature and the audience. Then, copy the claims you need
// to the "identity" instance and call SetDestinations on each claim to
// allow them to be persisted to either access or identity tokens (or both).
//
// Note: the identity MUST contain a "sub" claim containing the user ID.
var principal = new ClaimsPrincipal(identity);
foreach (var claim in principal.Claims)
{
claim.SetDestinations(claim.Type switch
{
"name" => new[]
{
Destinations.AccessToken,
Destinations.IdentityToken
},
_ => new[] { Destinations.AccessToken },
});
}
return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
}
throw new InvalidOperationException("The specified grant type is not supported.");
}
Note that you'll also have to enable it in the OpenIddict server options:
services.AddOpenIddict()
// ...
.AddServer(options =>
{
// ...
options.AllowCustomFlow("urn:ietf:params:oauth:grant-type:google_identity_token");
});
When sending a token request, make sure to use the right grant_type and to send your id_token as the assertion parameter, and it should work. Here's an example with Postman (for Facebook access tokens, but it works exactly the same way):
That said, you have to be extremely careful when implementing the token validation routine, as this step is particularly error-prone. It's really important to validate everything, including the audience (otherwise, your server would be vulnerable to confused deputy attacks).

Related

How to add custom claims to Jwt Token in OpenIdConnect in .Net Core

Just like AzureAD we have our own custom Firm ActiveDirectory which we are connecting from UI as well as API for Authentication in .NetCore using OpenIdConnect (AddOpenIdConnect extension method).
In my use case after authentication on UI side, I need additional application specific claims from my custom database which I am adding "OnTokenValidated" - this is needed for hiding or exposing the UI elements based on Roles and Claims.
OnTokenValidated = async ctx =>
{
//Get user's immutable object id from claims that came from Azure AD
string oid = ctx.Principal.FindFirstValue("http://schemas.microsoft.com/identity/claims/objectidentifier");
//Get EF context
var db = ctx.HttpContext.RequestServices.GetRequiredService<AuthorizationDbContext>();
//Check is user a super admin
bool isSuperAdmin = await db.SuperAdmins.AnyAsync(a => a.ObjectId == oid);
if (isSuperAdmin)
{
//Add claim if they are
var claims = new List<Claim>
{
new Claim(ClaimTypes.Role, "superadmin")
};
var appIdentity = new ClaimsIdentity(claims);
ctx.Principal.AddIdentity(appIdentity);
}
}
Now after token validation on API side again I have to call the custom database to fetch application specific roles. Is it possible to include these roles in JWT token itself so on API side all roles and claims(AD + Custom DB) are present. Or any other way by which I don't have to call CustomDB again in API.
Adding custom claims to access tokens is a capability of the Authorization Server (AS) and not all of them support this - though they should since it is an important feature. If you can say exactly what provider you are using I may be able to tell you whether it is possible.
These are the factors to think about:
Find out if the AS can reach out and get custom claims at the time of token issuance as in this Curity article.
Be careful to not return detailed JWTs to internet clients and aim to keep them within your back end instead. Opaque tokens can help with this as in this other Curity article
If custom claims are not possible for your provider then you can look them up in your API(s) when an access token is first received and then cache the claims in memory. This leads to more complex API code but is necessary for some providers.

Token based implementation in webapi to secure endpoints

I am having a web application with web service and client will register their application using my web application.
Now client will have application of type SPA or mobile apps and they will consume my webservices from their apps.
So I would be implementing token based mechanism for securing access to my endpoints.
1) But here I am confused that shall I use any framework to generate access token or I can use any library which will generate any random string which i will send in response.for instance something like this :
TokenId = Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Replace("+", "_")
So while registering application if client have enabled authentication for their application then user will be validated and then I will return access token and also save accesstoken in my database with that user id.
So my database table would be like below for storing validated accesstoken :
Id(autogenerated) accesstoken userid clientid createdat expiresat
So after user is authenticated and now if user want to access any protected resources then user need to pass this access token in subsequent call in header.
So what I will do I will get access token from header and then validate that accesstoken against that database and then allow access to my protected resource other wise user would get authorized.
I have seen lots of things related to this so basically this is oauth2 and I want to implement this.
I have seen Openid connect(this project doesnt even compile) which is on top of oauth2 and which is used for authentication and oauth2 will be used for authorization.
But here as I am storing access token in my database so here is my doubt related to that :
2) Now do I need openconnectid (but this project doesn't even compile) for validating access token or as I am having storing access token in my database I don't need openconnectid?
3) I want to implement asp.net identity but then I will receive dynamic database connection string and as i have seen asp.net identity mostly works with entity framework I couldn't find any source where I could use ado.net to validate username and password using SQL query. I know I can do something like this :
Make a custom user class which implements IUser as described here
Define a custom user store which implements
public class UserStoreService
: IUserStore<CustomUser>, IUserPasswordStore<CustomUser>
But I won't be having this information as I don't have fixed connection string.connection string again is stored in database with client registration.
4) We have given user a fixed endpoint through which client can create an admin so for that I will use my RSA algorithm for password hashing and then store it in database. So with this now do i need to use asp.net identity?
5) I have seen lots of following link with token based implementation but I am not getting where they are validating accesstoken in which part but now as I am having accesstoken stored in my database do I need to use any of the following implementation?
http://bitoftech.net/2014/10/27/json-web-token-asp-net-web-api-2-jwt-owin-authorization-server/
http://bitoftech.net/2014/06/01/token-based-authentication-asp-net-web-api-2-owin-asp-net-identity/
6) Moreover if for any client if client don't want authentication for its respective application then what I will do is I will don't have that username password validation but I will simply generate accesstoken and then send in response so then in every subsequent request that access token will be pass to access protected resources.Do you think this make sense?
I have never seen any example where access token is store in database and problem with storing access token in database would be I have to make a call to database every time to validate access token for each endpoint.
Update :
Use case of my webservice engine would be:
1) Support multiple client application.
2) Manage user session in the form of token management for each client application. So here as most of the article is storing accesstoken in identity and that identity is validated inside [Authorize] attribute in which accesstoken is also validated and based on that user is allowed to access protected resources.This is what my understanding is up until now.
So if I also user identity and store user context inside identity supporting multiple client application is a good idea?
From “7.1. Access Token Representation” in “Full-Scratch Implementor of OAuth and OpenID Connect Talks About Findings” :
How should an access token be represented? There are two major ways.
As a meaningless random string. Information associated with an access
token is stored in a database table behind an authorization server.
As a self-contained string which is a result of encoding access token
information by base64url or something similar.
Pros and cons of these two ways are described in the blog.
If access tokens are random strings, pieces of information associated with the access tokens (user ID, client ID, scopes, lifetime, etc.) are stored in a database which is managed by the authorization server which have issued the access tokens.
Whenever a resource server which exposes APIs accepts an API call from a client application, the resource server has to get the information about the access token in some way or other.
If the resource server can access the database managed by the authorization server (in other words, if the resource server and the authorization server shares the database), the resource server can get the information about the access token from the database directly.
Otherwise, the resource server has to make an API call to the authorization server to get the information. In this case, it can be expected that the authorization server exposes an API which complies with RFC 7662 (OAuth 2.0 Token Introspection). Note that some implementations may provide a more developer-friendly API than RFC 7662 (e.g. “4. Introspection Access Token”).
Anyway, your resource server doesn't necessarily have to make a DB call (or an introspection API call to the authorization server) every time if the server caches information about access tokens in a memory cache or somewhere else appropriate.
BTW, what you need when you want to protect APIs is access tokens. Therefore, your system doesn't have to support OpenID Connect which is a specification as to how to request and issue ID tokens. You may be confused because a server which supports OpenID Connect can issue access tokens, too, in addition to ID tokens. See “Diagrams of All The OpenID Connect Flows” to understand what a server which supports OpenID Connect issues.
Finally, identity management, user authentication, and OAuth 2.0 & OpenID Connect don't necessarily have to be implemented in a monolithic way. See “New Architecture of OAuth 2.0 and OpenID Connect Implementation” for details.
No, you don't need to store the access_token on the database. You can decrypt the JWT and read the information as you are the one who encrypts it with a secret key. (By default it's the machine key.)
Identity has a off the self support for Oauth. You have to just configure it properly. You can set-up the configuration for OAuthAuthorizationServerOptions in the Startup.Auth.cs. Sample code as follows. I have tried to answer most of your question in comments in the code.
public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
public static string PublicClientId { get; private set; }
public void ConfigureOAuth(IAppBuilder app)
{
// Configure the application for OAuth based flow
PublicClientId = "theDragonIsAlive";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new YourOwnApplicationOAuthProvider(PublicClientId),
//AuthorizeEndpointPath = new PathString("/Access/Account"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(7)
//AllowInsecureHttp = true
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
}
public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
{
private readonly string _publicClientId;
public ApplicationOAuthProvider(string publicClientId)
{
if (publicClientId == null)
{
throw new ArgumentNullException("publicClientId");
}
_publicClientId = publicClientId;
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
// This where you are validating the username and password credentials.
ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("Dragon Fire:", "The user name or password is incorrect. You shall be burnt.");
return;
}
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager,
OAuthDefaults.AuthenticationType);
AuthenticationProperties properties = CreateProperties(user.UserName);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(oAuthIdentity);
}
public override Task TokenEndpoint(OAuthTokenEndpointContext context)
{
foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
{
context.AdditionalResponseParameters.Add(property.Key, property.Value);
}
return Task.FromResult<object>(null);
}
// This method is where you will create the client access token.
// First you get the client, you can place values from the client record into the tokens claim collection.
// You then create a new ClaimsIdentity.
// You add some claims, in the example client name is added.
// Create an AuthenticationTicket using your claims identity.
// Validate the ticket (you do need to do this or the client will be considered unauthenticated)
//public override Task GrantClientCredentials(OAuthGrantClientCredentialsContext context)
//{
// var client = clientService.GetClient(context.ClientId);
// var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
// oAuthIdentity.AddClaim(new Claim(ClaimTypes.Name, client.ClientName));
// var ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties());
// context.Validated(ticket);
// return base.GrantClientCredentials(context);
//}
// This method has to be implmented when you are maintaining a list of clients which you will allow.
// This method is for validating the input, you can used this method to verify the client id and secret are valid.
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
//string clientId;
//string clientSecret;
//context.TryGetFormCredentials(out clientId, out clientSecret);
//if (clientId == "1234" && clientSecret == "12345")
//{
// context.Validated(clientId);
//}
//return base.ValidateClientAuthentication(context);
// Resource owner password credentials does not provide a client ID.
if (context.ClientId == null)
{
context.Validated();
}
return Task.FromResult<object>(null);
}
public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
{
if (context.ClientId == _publicClientId)
{
Uri expectedRootUri = new Uri(context.Request.Uri, "/");
if (expectedRootUri.AbsoluteUri == context.RedirectUri)
{
context.Validated();
}
}
return Task.FromResult<object>(null);
}
public static AuthenticationProperties CreateProperties(string userName)
{
IDictionary<string, string> data = new Dictionary<string, string>
{
{ "userName", userName }
};
return new AuthenticationProperties(data);
}
}
The sample code above does not have separate client classifications. It will treat all users as a single type of client. But I have given some example code in the comments which will guide you to get started in the right direction.
Disclaimer: I am not an expert on this(yet) and my setup is different. I had an existing MVC application with Owin and I had to build a webapi on top of it. This was my prototype code and it did the job. You will have to improve in it for your production code. Have fun and good luck.

.NET Identity: Generate token without explicit username / password

I have a MVC application configured to use ASP.NET Identity. More specifically, I've configured the application to use OAuth. Everything is working perfectly as-is. I can pass a username/password to my token endpoint and receive a token in return. I have refresh tokens configured, I stripping default claims and adding my own, authorization attributes implemented, etc.
I have a new requirement to generate a token based on non- username / password combination. The combination is similar to username/password at a high level: user passes two pieces of about themselves and, if true, a JWT is returned (just like with username/password).
Let's say I have a Person and AspNetUser table. Some Person records will be AspNetUsers, but not all of them. The ones that are not would like to authenticate using the method I described above. I guess, technically they can be AspNetUser records but with no configured password and possibly email.
I'm having difficulty coming up with a proper solution to the problem. Do I create a custom endpoint to take this alternative auth credentials and return a bearer token? If so, any advice on the proper way to leverage .NET Identity to return the token? Do I modify my custom OAuthAuthorizationServerProvider to account for this alternate method? If so, any advice on creating a "fake" (i.e. user does not exist in the AspNet* tables) identity to be return to the client?
As always, any help is appreciated.
Thanks!
#Zenichi requested how I solved this so I'm adding an answer. Hindsight, there are probably better ways to solve this but this is what I landed on.
I added custom controller which returned responses similar to that of the Identity Framework. I'm basically just building a ticket then creating an access token, then returning to the caller. Here is a snippet:
var properties = new AuthenticationProperties(new Dictionary<string, string>
{
{
"audience", string.Empty
}
});
var ticket = new AuthenticationTicket(identity, properties)
{
Properties =
{
IssuedUtc = DateTime.UtcNow,
ExpiresUtc = DateTime.UtcNow.AddMinutes(Config.TokenLifeSpan)
};
var accessToken = new ApplicationJwtFormat(Config.Issuer).Protect(ticket);
var expiresIn = Config.TokenLifeSpan * 60 - 1;
var token = new OAuthSuccessResponse(accessToken, "bearer", Convert.ToInt32(expiresIn), null);
return Content(HttpStatusCode.OK, token);
public OAuthSuccessResponse(string accessToken, string tokenType, int expiresIn, string refreshToken)
{
access_token = accessToken;
token_type = tokenType;
expires_in = expiresIn;
refresh_token = refreshToken;
}
In looking at it again, I'd probably would have (at least) pascal cased the property names of OAuthSuccessResponse and used the [JsonProperty] attribute to control casing during serialization.
Going further - I probably should have scrapped this approach and just continue to extend my OAuthProvider implementation to handle both traditional user/passwords and whatever other combination I needed.
Hope this helps.

MVC 5 Reverse Proxy Authentication and make use of Roles, by using the username only

I have an MVC 5 web app. Currently the application is using Individual Accounts to login. There are also roles in place for authorization. The users login using their username and password.
The problem is like this. I need to authenticate and authorize users requests that are coming via a reverse proxy.
User make's a request to for the app (reverse proxy url) http://myapp.domain.com
Reverse proxy will make additional calls and verify that the user is authorized to access the app content. (missing from diagram for simplicity).
If everything ok with the user the call is redirected to the actual MVC application where the request will contain a header with the username.
The MVC must check that the request is coming from the reverse proxy (not a problem to do that, IP check, plus some key sent as headers.)
MVC should read the request header, get the username, authenticate it and authorize it based on the role or roles he has.
Deny the request if the request doesn't come from the reverse proxy, deny the request if the user doesn't have appropriate roles. Example user is in role Visitor but he's trying to access admin role content.
My problem is in the authentication and authorization of the request when there is present just the username.
As the application uses already username and password for authentication, I'm thinking to do the following:
The reverse proxy will send the request to https://realapp.domain.com/account/login.
In the action login from account controller I can implement the logic to read the request and get the username from the header
At this point we know that user X is authenticated because the reverse proxy and additional systems will check that. So basically all requests arriving are considered safe (from the reverse proxy server)
If the username doesn't exists within the database (first time call to the application) the MVC app will create the user with a dummy password (Password123).
If the username exists within the database then log him in using the username and the dummy password Password123 var result = await SignInManager.PasswordSignInAsync("username", "Password123", false, shouldLockout: false);
User is authenticated and authorized.
Basically my idea is to set for each user same password in order to authenticate them within the application and make use of roles.
What do you think?
Since Identity is claim based. You don't need any password or even any user object to authenticate users. So using storage in Identity is totally optional also. You just need create some claims and authorize your users based on those. Consider this simple example as a clue:
// imaging this action is called by proxy
public ActionResoult Login()
{
// this custom method extract username from header and check IP and more
var username=_myUserManager.GetUserName();
if(username!=null)
{
// optionally you have own user manager which returns roles from username
// no matter how you store users and roles
string[] roles=_myUserManager.GetUserRoles(username);
// user is valid, going to authenticate user for my App
var ident = new ClaimsIdentity(
new[]
{
// adding following 2 claim just for supporting default antiforgery provider
new Claim(ClaimTypes.NameIdentifier, username),
new Claim("http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider", "ASP.NET Identity", "http://www.w3.org/2001/XMLSchema#string"),
// an optional claim you could omit this
new Claim(ClaimTypes.Name, username),
// populate assigned user's role form your DB
// and add each one as a claim
new Claim(ClaimTypes.Role, roles[0]),
new Claim(ClaimTypes.Role, roles[1]),
// and so on
},
DefaultAuthenticationTypes.ApplicationCookie);
// Identity is sign in user based on claim don't matter
// how you generated it
HttpContext.GetOwinContext().Authentication.SignIn(
new AuthenticationProperties { IsPersistent = false }, ident);
// auth is succeed, without needing any password just claim based
return RedirectToAction("MyAction");
}
// invalid user
ModelState.AddModelError("", "We could not authorize you :(");
return View();
}
Now we authorized our users without any password so we could use Authorize filter as well:
[Authorize]
public ActionResult Foo()
{
}
// since we injected user roles to Identity we could do this as well
[Authorize(Roles="admin")]
public ActionResult Foo()
{
// since we injected our authentication mechanism to Identity pipeline
// we have access current user principal by calling also
// HttpContext.User
}
Note: Your proxy must handle your apps generated cookies and deliver them to your users properly. Since your app based on cookie.
You could download Token Based Authentication Sample from my Github repo and also read my other answer which is closes to your scenario.

Validating Google ID tokens in C#

I need to validate a Google ID token passed from a mobile device at my ASP.NET web api.
Google have some sample code here but it relies on a JWT NuGet package which is .Net 4.5 only (I am using C#/.Net 4.0). Is anyone aware of any samples which do this without these packages or has achieved this themselves? The use of the package makes it very difficult to work out what I need to do without it.
According to this github issue, you can now use GoogleJsonWebSignature.ValidateAsync method to validate a Google-signed JWT. Simply pass the idToken string to the method.
var validPayload = await GoogleJsonWebSignature.ValidateAsync(idToken);
Assert.NotNull(validPayload);
If it is not a valid one, it will return null.
Note that to use this method, you need to install Google.Apis.Auth nuget firsthand.
The challenge is validating the JWT certificate in the ID token. There is currently not a library I'm aware of that can do this that doesn't require .Net 4.5 and until there is a solution for JWT validation in .NET 4.0, there will not be an easy solution.
However, if you have an access token, you can look into performing validation using oauth2.tokeninfo. To perform basic validation using token info, you can do something like the following:
// Use Tokeninfo to validate the user and the client.
var tokeninfo_request = new Oauth2Service().Tokeninfo();
tokeninfo_request.Access_token = _authState.AccessToken;
var tokeninfo = tokeninfo_request.Fetch();
if (userid == tokeninfo.User_id
&& tokeninfo.Issued_to == CLIENT_ID)
{
// Basic validation succeeded
}
else
{
// The credentials did not match.
}
The information returned from the Google OAuth2 API tells you more information about a particular token such as the client id it was issued too as well as its expiration time.
Note You should not be passing around the access token but instead should be doing this check after exchanging a one-time code to retrieve an access token.
ClientId also needs to be passed, which should be set from Google API Console. If only pass TokenId, GoogleJsonWebSignature throws error. This answer is in addition to #edmundpie answer
var settings = new GoogleJsonWebSignature.ValidationSettings()
{
Audience = new List<string>() { "[Placeholder for Client Id].apps.googleusercontent.com" }
};
var validPayload = await GoogleJsonWebSignature.ValidateAsync(model.ExternalTokenId, settings);

Categories

Resources