Taylohtio/GeneralSSO/GeneralSSO.Server/CodeFiles/Infrastructure/OAuth/AuthorizationServerHost.cs

172 lines
8.7 KiB
C#

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Security.Cryptography;
using System.Web;
using DotNetOpenAuth.Messaging.Bindings;
using DotNetOpenAuth.OAuth2;
using DotNetOpenAuth.OAuth2.ChannelElements;
using DotNetOpenAuth.OAuth2.Messages;
using Microsoft.Practices.ServiceLocation;
using Taloyhtio.GeneralSSO.Server.CodeFiles.Common;
using Taloyhtio.GeneralSSO.Server.CodeFiles.Repositories;
namespace Taloyhtio.GeneralSSO.Server.CodeFiles.Infrastructure.OAuth
{
public class AuthorizationServerHost : IAuthorizationServerHost
{
private IClientRepository clientRepository;
private IClientAuthorizationRepository clientAuthorizationRepository;
public ICryptoKeyStore CryptoKeyStore
{
get { return ServiceLocator.Current.GetInstance<ICryptoKeyStore>(); }
}
public INonceStore NonceStore
{
get { return ServiceLocator.Current.GetInstance<INonceStore>(); }
}
public AuthorizationServerHost()
{
this.clientRepository = ServiceLocator.Current.GetInstance<IClientRepository>();
this.clientAuthorizationRepository = ServiceLocator.Current.GetInstance<IClientAuthorizationRepository>();
}
// Create access token:
// 1. Authorization server: signs token with auth server's private key (cert contains both private and public key)
// 2. Authorization server: encrypts token with resource server's public key only (cert contains public key only)
// 3. Resource server: decrypts token with resource server's private key (cert contains both private and public key)
// 4. Resource server: validates token with auth server's public key (cert contains public key only)
// In all-in-one server environments auth and res certificates contain both private and public keys
public AccessTokenResult CreateAccessToken(IAccessTokenRequest accessTokenRequestMessage)
{
var accessToken = new AuthorizationServerAccessToken();
// Just for the sake of the sample, we use a short-lived token. This can be useful to mitigate the security risks
// of access tokens that are used over standard HTTP.
// But this is just the lifetime of the access token. The client can still renew it using their refresh token until
// the authorization itself expires.
int lifetimeMinutes = this.getTokenLifetime();
accessToken.Lifetime = TimeSpan.FromMinutes(lifetimeMinutes);
// Also take into account the remaining life of the authorization and artificially shorten the access token's lifetime
// to account for that if necessary.
// For this sample, we assume just one resource server.
// If this authorization server needs to mint access tokens for more than one resource server,
// we'd look at the request message passed to us and decide which public key to return.
accessToken.ResourceServerEncryptionKey = (RSACryptoServiceProvider)Cert.ResourceServerEncyptionCertificate.PublicKey.Key;
accessToken.AccessTokenSigningKey = (RSACryptoServiceProvider)Cert.AuthServerSigningCertificate.PrivateKey;
var result = new AccessTokenResult(accessToken);
return result;
}
private int getTokenLifetime()
{
string lifeTimeStr = ConfigurationManager.AppSettings["AccessTokenLifeTimeMinutes"];
if (string.IsNullOrEmpty(lifeTimeStr))
{
return Constants.OAuth.DEFAULT_TOKEN_LIFETIME_MINUTES;
}
int lifeTime;
if (!int.TryParse(lifeTimeStr, out lifeTime))
{
return Constants.OAuth.DEFAULT_TOKEN_LIFETIME_MINUTES;
}
return lifeTime;
}
public IClientDescription GetClient(string clientIdentifier)
{
// var consumerRow = ServerContext.DataContext.Clients.SingleOrDefault(
// consumerCandidate => consumerCandidate.ClientIdentifier == clientIdentifier);
// if (consumerRow == null)
// {
// throw new ArgumentOutOfRangeException("clientIdentifier");
// }
//
// return consumerRow;
var client = this.clientRepository.GetByClientId(clientIdentifier);
if (client == null)
{
throw new Exception(string.Format("Client '{0}' not registered.", clientIdentifier));
}
return client;
}
public bool IsAuthorizationValid(IAuthorizationDescription authorization)
{
return this.isAuthorizationValid(authorization.Scope, authorization.ClientIdentifier, authorization.UtcIssued, authorization.User);
}
private bool isAuthorizationValid(HashSet<string> requestedScopes, string clientIdentifier, DateTime issuedUtc, string username)
{
// If db precision exceeds token time precision (which is common), the following query would
// often disregard a token that is minted immediately after the authorization record is stored in the db.
// To compensate for this, we'll increase the timestamp on the token's issue date by 1 second.
issuedUtc += TimeSpan.FromSeconds(1);
// var grantedScopeStrings = from auth in ServerContext.DataContext.ClientAuthorizations
// where
// auth.Client.ClientIdentifier == clientIdentifier &&
// auth.CreatedOnUtc <= issuedUtc &&
// (!auth.ExpirationDateUtc.HasValue || auth.ExpirationDateUtc.Value >= DateTime.UtcNow) &&
// auth.User.OpenIDClaimedIdentifier == username
// select auth.Scope;
var grantedScopeStrings = this.clientAuthorizationRepository.GetNotExpiredBy(clientIdentifier, username, issuedUtc).Select(c => c.Scope);
if (grantedScopeStrings.IsNullOrEmpty())
{
// No granted authorizations prior to the issuance of this token, so it must have been revoked.
// Even if later authorizations restore this client's ability to call in, we can't allow
// access tokens issued before the re-authorization because the revoked authorization should
// effectively and permanently revoke all access and refresh tokens.
return false;
}
var grantedScopes = new HashSet<string>(OAuthUtilities.ScopeStringComparer);
grantedScopeStrings.ToList().ForEach(c => grantedScopes.UnionWith(OAuthUtilities.SplitScopes(c)));
return requestedScopes.IsSubsetOf(grantedScopes);
}
/*public bool CanBeAutoApproved(EndUserAuthorizationRequest authRequest, string userName)
{
if (authRequest == null || string.IsNullOrEmpty(userName))
{
return false;
}
// NEVER issue an auto-approval to a client that would end up getting an access token immediately
// (without a client secret), as that would allow arbitrary clients to masquarade as an approved client
// and obtain unauthorized access to user data.
if (authRequest.ResponseType == EndUserAuthorizationResponseType.AuthorizationCode)
{
// Never issue auto-approval if the client secret is blank, since that too makes it easy to spoof
// a client's identity and obtain unauthorized access.
//var requestingClient = ServerContext.DataContext.Clients.First(c => c.ClientIdentifier == authorizationRequest.ClientIdentifier);
var requestingClient = this.clientRepository.GetBy(authRequest.ClientIdentifier);
if (requestingClient == null || string.IsNullOrEmpty(requestingClient.ClientSecret))
{
return false;
}
return this.isAuthorizationValid(authRequest.Scope, authRequest.ClientIdentifier, DateTime.UtcNow, userName);
}
// Default to not auto-approving.
return false;
}*/
public AutomatedAuthorizationCheckResponse CheckAuthorizeClientCredentialsGrant(IAccessTokenRequest accessRequest)
{
throw new NotImplementedException();
}
public AutomatedUserAuthorizationCheckResponse CheckAuthorizeResourceOwnerCredentialGrant(string userName, string password, IAccessTokenRequest accessRequest)
{
throw new NotImplementedException();
}
}
}