Caching issuer and keys from the metadata endpoint - azure-active-directory

I followed the sample for calling an ASP.NET Web API from an ASP.NET Web App using the Azure AD B2C:
https://github.com/Azure-Samples/active-directory-b2c-dotnet-webapp-and-webapi
I've a question regarding the OpenIdConnectCachingSecurityTokenProvider
// This class is necessary because the OAuthBearer Middleware does not leverage
// the OpenID Connect metadata endpoint exposed by the STS by default.
public class OpenIdConnectCachingSecurityTokenProvider : IIssuerSecurityKeyProvider
{
public ConfigurationManager<OpenIdConnectConfiguration> _configManager;
private string _issuer;
private IEnumerable<SecurityKey> _keys;
private readonly string _metadataEndpoint;
private readonly ReaderWriterLockSlim _synclock = new ReaderWriterLockSlim();
public OpenIdConnectCachingSecurityTokenProvider(string metadataEndpoint)
{
_metadataEndpoint = metadataEndpoint;
_configManager = new ConfigurationManager<OpenIdConnectConfiguration>(metadataEndpoint, new OpenIdConnectConfigurationRetriever());
RetrieveMetadata();
}
/// <summary>
/// Gets the issuer the credentials are for.
/// </summary>
/// <value>
/// The issuer the credentials are for.
/// </value>
public string Issuer
{
get
{
RetrieveMetadata();
_synclock.EnterReadLock();
try
{
return _issuer;
}
finally
{
_synclock.ExitReadLock();
}
}
}
/// <summary>
/// Gets all known security keys.
/// </summary>
/// <value>
/// All known security keys.
/// </value>
public IEnumerable<SecurityKey> SecurityKeys
{
get
{
RetrieveMetadata();
_synclock.EnterReadLock();
try
{
return _keys;
}
finally
{
_synclock.ExitReadLock();
}
}
}
private void RetrieveMetadata()
{
_synclock.EnterWriteLock();
try
{
OpenIdConnectConfiguration config = Task.Run(_configManager.GetConfigurationAsync).Result;
_issuer = config.Issuer;
_keys = config.SigningKeys;
}
finally
{
_synclock.ExitWriteLock();
}
}
}
The metadata endpoint:
https://login.microsoftonline.com/{TENANT}.onmicrosoft.com/v2.0/.well-known/openid-configuration?p={POLICY}
Why all the time we need to make a call to retrieve the keys and the issuer?
Can I cache these values?
If yes, what's the best setting for the expiration?

Why all the time we need to make a call to retrieve the keys and the
issuer?
Signing key: Your app must use this signing key(public key) to validate the token which is signed by AAD using its private key. This metadata endpoint contains all the public key information in use at the particular moment:
https://login.microsoftonline.com/<yourtenantdomain>/discovery/v2.0/keys?p=<SigninPolicyName>
Issuer : Your application needs Issuer to validate the token's iss claim to trust this token. Issuer can be also retrieved from the OpenID connect metadata endpoint:
https://login.microsoftonline.com/<YourTenantDomain>/v2.0/.well-known/openid-configuration?p=<SigninPolicyName>
Identifies the security token service (STS) that constructs and
returns the token. In the tokens that Azure AD returns, the issuer is
sts.windows.net. The GUID in the Issuer claim value is the tenant ID
of the Azure AD directory. The tenant ID is an immutable and reliable
identifier of the directory.
Also,OAuthBearer Middleware doesn't leverage this metadata endpoint by default, so you need to retrieve it with code. So, you must retrieve the keys and the issuer to validate the token.
Can I cache these values? If yes, what's the best setting for the
expiration?
Yes, with the code you post, it cache these values in configManager.GetConfigurationAsync and OpenIdConnectCachingSecurityTokenProvider use it when starting up.
About the expiration: Signing key can roll over. So, don't worry about the set expiration for the singing key. The important thing is that you'd better to fetch the metadata location dynamically to keep the signing key is the correct.
Reference:
You can see the details about Validate the signature of B2C tokens in this documentaion.
See more details about Signing key rollover in AAD in this documentation.
See more details about OpendID Provider Metadata :http://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata

Related

getting to grips with Quickbooks Hello World app, all working ok but I have what I think is an easy question

in application.properties I need to set the OAuth2 keys...
OAuth2AppClientId=AB............................AN
OAuth2AppClientSecret=br................................u8
OAuth2AppRedirectUri=http://localhost:8085/oauth2redirect
Initially I put the keys in "" quotes assuming they should be treated as a string but to get it working I had to remove them. Can someone explain what's happening with
OAuth2AppClientId=AB............................AN when I build the app
and how do I find out more about OAuth2AppClientId?
A Google search is probably the place to start here. Here's a great resource about what a Client ID and Client Secret are:
https://www.oauth.com/oauth2-servers/client-registration/client-id-secret/
I quote:
The client_id is a public identifier for apps.
The client_secret is a secret known only to the application and the authorization server.
Intuit also has a ton of documentation on OAuth2, and how to implement it. You should read it:
https://developer.intuit.com/app/developer/qbo/docs/develop/authentication-and-authorization/oauth-2.0
In summary, the Client ID is how Intuit identifies that it's your app trying to connect to QuickBooks. Nothing is "happening" to the string when you build/compile the app - it's just normal string. But when your app authenticates against QuickBooks Online, your app sends the Client ID to QuickBooks so that QuickBooks knows it's your app trying to authorize a connection to QuickBooks, and not some other app.
If you want to see how to code is loading this, it is only a property being used inside the application
OAuth2PlatformClientFactory
#Service
#PropertySource(value="classpath:/application.properties", ignoreResourceNotFound=true)
public class OAuth2PlatformClientFactory {
#Autowired
org.springframework.core.env.Environment env;
OAuth2PlatformClient client;
OAuth2Config oauth2Config;
#PostConstruct
public void init() {
// intitialize a single thread executor, this will ensure only one thread processes the queue
oauth2Config = new OAuth2Config.OAuth2ConfigBuilder(env.getProperty("OAuth2AppClientId"), env.getProperty("OAuth2AppClientSecret")) //set client id, secret
.callDiscoveryAPI(Environment.SANDBOX) // call discovery API to populate urls
.buildConfig();
client = new OAuth2PlatformClient(oauth2Config);
}
public OAuth2PlatformClient getOAuth2PlatformClient() {
return client;
}
public OAuth2Config getOAuth2Config() {
return oauth2Config;
}
public String getPropertyValue(String propertyName) {
return env.getProperty(propertyName);
}
}
https://github.com/IntuitDeveloper/OAuth2-JavaWithSDK/blob/master/src/main/java/com/intuit/developer/sampleapp/oauth2/client/OAuth2PlatformClientFactory.java

IdentityServer 4 Restfull Login/Logout

Been looking into the identity server 4 solution to compliment my ASP CORE api.
Using a SPA page on front end, does IdentityServer4 have the capability to manage restfull calls for login/logout/other?
Currently my solution works perfectly to redirect to and from the IdentityServer4 solution, but wondering if i can improve on UX by avoiding the redirects that occur on login/logout?
I've heard of PopUp and iFrame capability, but from research that opens up other risks.
(not sure if this question is for stackoverflow or software engineering stack, happy to move it)
You may do this by using the resource owner password grant type, where you could provide your own login screen and pass the information to IdentityServer.
In IdentityServer you would implement the IResourceOwnerPasswordValidator interface to validate the users.
In your Startup.ConfigureServices add the following.
Services.AddTransient<IResourceOwnerPasswordValidator, ResourceOwnerPasswordValidator>();
Here is a sample ResourceOwnerPasswordValidator class.
public class ResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator
{
private IUserManager _myUserManager { get; set; }
public ResourceOwnerPasswordValidator(IUserManager userManager)
{
_myUserManager = userManager;
}
public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
{
var user = await _myUserManager.Find(context.UserName, context.Password);
if (user != null)
{
context.Result = new GrantValidationResult(
subject: user.USER_ID,
authenticationMethod: "custom",
claims: await _myUserManager.GetClaimsAsync(user));
}
else
{
context.Result = new GrantValidationResult(
TokenRequestErrors.InvalidRequest,
errorDescription: "UserName or Password Incorrect.");
}
}
}
The IUserManager implements the logic to check the database to validate the user.
Then the SPA client would use the GrantTypes.ResourceOwnerPassword. Here is an example you could start with.
DISCLAIMER
This is not the recommended flow to use.

Managing user authentication in a GAE application

What is the best way to manage a user session in a Google App Engine application? Ideally I'd like to keep my application stateless and not save any user related data in memory, however I'm also afraid to send user credentials of the network on every request (not to mention authenticating the user on every request would require a call to the Datastore which costs money).
I checked out google's OAuth 2.0 solution but from my understanding it helps if my application wants to connect to any of the google APIs and needs permission from the client to access his google account.
Is there a go to way for managing user session? The most common scenario is to know which user initiated this request without having to send the userId as a request parameter.
Please note that we are not using third party providers. The user registers himself to our page normally and has a custom account. I'm not looking for tools that help integrate authentication with third party services. Otherwise I'd be using google's OAuth 2.0 or similar API
You can Always implement Authenticator Interface.
public class MyAuthenticator implements Authenticator {
#Override
public User authenticate(HttpServletRequest request) {
HttpSession session = request.getSession(false);
//
return null;// if not authenticated, otherwise return User object.
}
}
// Endpoints class.
#Api(name = "example", authenticators = { MyAuthenticator.class })
public class MyEndpoints {
public Profile getProfile(User user) {
if (user == null) {
throw new UnauthorizedException("Authorization required");
}
return new Profile(user.getEmail(), "displayName");
}
// store this class somewhere in models
public class Profile {
private String email;
private String displayName;
public Profile(String email, String displayName) {
this.email = email;
this.displayName = displayName;
}
public String getEmail() {
return email;
}
public String getdisplayName() {
return displayName;
}
}
}
Use the HttpServletRequest object to implement classic session based login or use your own custom header. Well that depends on your case. Return null when not authenticated and return User object when authenticated. Also implement some kind of encryption on both sides(client and server), so as to stop someone having the session key to access your api.

Preferred method for persist session token on the server with RIA Services?

I'm using an AuthenticationService derived from AuthenticationBase in the standard business application template for RIA Services and using Forms authentication. I have my own CustomPrincipal that is created by my security infrastructure that I assign to Thread.CurrentPrincipal, so that it can be used by other service calls:
protected override bool ValidateUser(string userName, string password)
{
try
{
using (LoginService service = new LoginService())
{
SessionInfo info = service.Login(userName, password);
Thread.CurrentPrincipal = info.User;
SessionCache.Instance.Save(info);
}
}
catch (Exception e)
{
return false;
}
}
I've discovered, however, when other (non-authentication) RIA services are called, the Thread.CurrentPrincipal is reset to a GenericPrincipal object, so upon initialization of the other services, I override the Initialize() method of the domain service and set Thread.CurrentPrincipal by looking up the login in a session cache:
public override void Initialize(DomainServiceContext context)
{
base.Initialize(context);
if (context.User.Identity.IsAuthenticated)
{
SessionInfo info = SessionCache.Instance.FindByUsername(context.User.Identity.Name);
Thread.CurrentPrincipal = info.User;
}
}
Right now this lookup is being done by Username, because it is easily accessible in the GenericPrincipal, but I'd prefer that I could do the lookup via a session token. Is there a method with RIA services to easily persist a session token on the server during the lifetime of the session? I know I could put this session token in a cookie in the browser to persist it, but it seems like there should be a simpler method to persist a session token across the lifetime of the session. Perhaps the cookie method is the best way to do this, but I just wanted to confirm.

Having a problem with RequiresRole attribute on RIA Domain service

My question is similar to this question. I hope I can provide some more detail and context to get it answered.
So here's some context: I have a simple in-house silverlight (ver 4) app with WCF Ria services that I'm building for our small support team. It uses authentication against a third-party vended database, but all other user information, e.g. FriendlyName and Roles (only 1 role per user) comes from our own database. I'm trying to keep this simple and don't want to implement custom membership and role providers.
I have few domain service operations that I want to restrict to certain roles, so I tried using the RequiresRole attribute like so:
[RequiresRole("Admin", "HelpDesk", "Billing" )]
public RisStudyInfo GetStudyInfo(string accession) {
return ris.GetStudyInfo(accession);
}
On the client side WebContext.Current.User.IsInRole("Admin") returns true, but I always get access denied when calling the service. The RequiresAuthentication attribute works as expected.
Below is the implementation of my AuthenticationService. The User class simply inherits from UserBase and adds the FriendlyName property. Any ideas what I'm doing wrong?
[EnableClientAccess]
public class AuthenticationService : AuthenticationBase<User> {
UserDataService userData = new UserDataService();
protected override bool ValidateUser(string userName, string password) {
var auth = new DatabaseAuthenticator();
return auth.Authenticate(userName, password);
}
protected override User GetAuthenticatedUser(IPrincipal principal) {
User user = null;
if (principal.Identity.IsAuthenticated) {
user = new User();
user.FriendlyName = userData.GetFriendlyName(principal.Identity.Name);
user.Name = principal.Identity.Name;
user.Roles = GetRolesFor(user.Name);
}
return user;
}
private IEnumerable<string> GetRolesFor(string username) {
IList<string> roles = new List<string>();
string role = userData.GetRolesFor(username);
if (role != null)
roles.Add(role);
return roles;
}
Figured it out. At least 2 things wrong. First clue found here. The second clue here
1.Turns out I really do need to write a custom role provider. Only need to implement GetRolesForUser though.
public override string[] GetRolesForUser(string username) {
return new string[] { _userService.GetRolesFor(username) };
}
2.Configure the custom role provider correctly in the web.config
<roleManager cacheRolesInCookie="true" enabled="true" defaultProvider="MyRoleProvider">
<providers>
<add name="MyRoleProvider" type="MyProject.Web.Providers.MyRoleProvider, MyProject.Web"/>
</providers>
</roleManager>
I solved this one by using the local credential store to cache credentials. Whenever a local cred check fails a foreign check occurs and the cache is populated/updated. This was a trivial override of the ValidateUser method. It does mean that stale passwords continue to work until the updated password is used (it will fail locally, pass remotely and trigger an update).
This approach meant that internally everything worked as per an out of the box configuration with no need for any other mods (apart from removing the local create-a-user links).

Resources