/// <summary> /// OAuth token request /// </summary> // TODO: Add ability to authentication a claim with POU public Stream Token(Message incomingMessage) { // Convert inbound data to token request // HACK: This is to overcome WCF's lack of easy URL encoded form processing // Why use WCF you ask? Well, everything else is hosted in WCF and we // want to be able to use the same ports as our other services. Could find // no documentation about running WCF and WepAPI stuff in the same app domain // on the same ports NameValueCollection tokenRequest = new NameValueCollection(); XmlDictionaryReader bodyReader = incomingMessage.GetReaderAtBodyContents(); bodyReader.ReadStartElement("Binary"); String rawBody = Encoding.UTF8.GetString(bodyReader.ReadContentAsBase64()); var parms = rawBody.Split('&'); foreach (var p in parms) { var kvp = p.Split('='); tokenRequest.Add(kvp[0], kvp[1].Replace('+', ' '). Replace("%3A", ":"). Replace("%2F", "/"). Replace("%3C", "<"). Replace("%3E", ">"). Replace("%21", "!"). Replace("%3D", "="). Replace("%5B", "["). Replace("%5D", "]").Trim()); } // Get the client application IApplicationIdentityProviderService clientIdentityService = ApplicationContext.Current.GetService <IApplicationIdentityProviderService>(); IIdentityProviderService identityProvider = ApplicationContext.Current.GetService <IIdentityProviderService>(); // Only password grants if (tokenRequest["grant_type"] != OAuthConstants.GrantNamePassword && tokenRequest["grant_type"] != OAuthConstants.GrantNameRefresh) { return(this.CreateErrorCondition(OAuthErrorType.unsupported_grant_type, "Only 'password' or 'refresh_token' grants allowed")); } // Password grant needs well formed scope Uri scope = null; if (String.IsNullOrWhiteSpace(tokenRequest["scope"]) || !Uri.TryCreate(tokenRequest["scope"], UriKind.Absolute, out scope)) { this.m_traceSource.TraceEvent(TraceEventType.Warning, 0, "Scope:{0} is not well formed", tokenRequest["scope"]); return(this.CreateErrorCondition(OAuthErrorType.invalid_scope, "Password grant must have well known scope")); } IPrincipal clientPrincipal = ClaimsPrincipal.Current; // Client is not authenticated if (clientPrincipal == null || !clientPrincipal.Identity.IsAuthenticated) { return(this.CreateErrorCondition(OAuthErrorType.unauthorized_client, "Unauthorized Client")); } this.m_traceSource.TraceInformation("Begin owner password credential grant for {0}", clientPrincipal.Identity.Name); if (this.m_configuration.AllowedScopes != null && !this.m_configuration.AllowedScopes.Contains(tokenRequest["scope"])) { return(this.CreateErrorCondition(OAuthErrorType.invalid_scope, "Scope not registered with provider")); } var appliesTo = new EndpointReference(tokenRequest["scope"]); // Validate username and password if (String.IsNullOrWhiteSpace(tokenRequest["username"]) && String.IsNullOrWhiteSpace(tokenRequest["refresh_token"])) { return(this.CreateErrorCondition(OAuthErrorType.invalid_request, "Invalid client grant message")); } else { try { IPrincipal principal = null; // Is there a TFA secret if (tokenRequest["grant_type"] == OAuthConstants.GrantNamePassword) { if (WebOperationContext.Current.IncomingRequest.Headers[OAuthConstants.TfaHeaderName] != null) { principal = identityProvider.Authenticate(tokenRequest["username"], tokenRequest["password"], WebOperationContext.Current.IncomingRequest.Headers[OAuthConstants.TfaHeaderName]); } else { principal = identityProvider.Authenticate(tokenRequest["username"], tokenRequest["password"]); } } else if (tokenRequest["grant_type"] == OAuthConstants.GrantNameRefresh && identityProvider is IIdentityRefreshProviderService) { var refreshToken = tokenRequest["refresh_token"]; // Verify signature! var signingCredentials = this.CreateSigningCredentials(); var signer = new JwtSecurityTokenHandler().SignatureProviderFactory.CreateForVerifying(signingCredentials.SigningKey, signingCredentials.SignatureAlgorithm); // Verify var tokenParts = refreshToken.Split('.').Select(o => Enumerable.Range(0, o.Length) .Where(x => x % 2 == 0) .Select(x => Convert.ToByte(o.Substring(x, 2), 16)) .ToArray() ).ToArray(); if (tokenParts.Length != 2) { throw new SecurityTokenException("Refresh token in invalid format"); } else if (!signer.Verify(tokenParts[1], tokenParts[0])) { throw new SecurityTokenValidationException("Signature does not match refresh token data"); } else { var secret = tokenParts[1]; principal = (identityProvider as IIdentityRefreshProviderService).Authenticate(secret); } } else { throw new InvalidOperationException("Invalid grant type"); } if (principal == null) { return(this.CreateErrorCondition(OAuthErrorType.invalid_grant, "Invalid username or password")); } else { var clams = this.ValidateClaims(principal); return(this.CreateTokenResponse(principal, clientPrincipal, appliesTo, this.ValidateClaims(principal))); } } catch (AuthenticationException e) { this.m_traceSource.TraceEvent(TraceEventType.Error, e.HResult, "Error generating token: {0}", e); return(this.CreateErrorCondition(OAuthErrorType.invalid_grant, e.Message)); } catch (SecurityException e) { this.m_traceSource.TraceEvent(TraceEventType.Error, e.HResult, "Error generating token: {0}", e); return(this.CreateErrorCondition(OAuthErrorType.invalid_grant, e.Message)); } catch (Exception e) { this.m_traceSource.TraceEvent(TraceEventType.Error, e.HResult, "Error generating token: {0}", e); return(this.CreateErrorCondition(OAuthErrorType.invalid_request, e.Message)); } } }