Skip to content
This repository has been archived by the owner on Sep 18, 2021. It is now read-only.

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
leastprivilege committed Jan 15, 2017
2 parents 93bc6bc + e434539 commit ad4fdcc
Show file tree
Hide file tree
Showing 15 changed files with 105 additions and 23 deletions.
2 changes: 1 addition & 1 deletion default.ps1
Expand Up @@ -11,7 +11,7 @@ properties {
$nuget_path = "$base_directory\nuget.exe"

$buildNumber = 0;
$version = "2.5.4.0"
$version = "2.6.0.0"
$preRelease = $null
}

Expand Down
2 changes: 0 additions & 2 deletions source/Core/Configuration/Hosting/ErrorPageFilterAttribute.cs
Expand Up @@ -31,8 +31,6 @@ internal class ErrorPageFilterAttribute : ExceptionFilterAttribute

public override async System.Threading.Tasks.Task OnExceptionAsync(HttpActionExecutedContext actionExecutedContext, System.Threading.CancellationToken cancellationToken)
{
Logger.ErrorException("Exception accessing: " + actionExecutedContext.Request.RequestUri.AbsolutePath, actionExecutedContext.Exception);

var env = actionExecutedContext.ActionContext.Request.GetOwinEnvironment();
var options = env.ResolveDependency<IdentityServerOptions>();
var viewSvc = env.ResolveDependency<IViewService>();
Expand Down
10 changes: 9 additions & 1 deletion source/Core/Configuration/Hosting/LogProviderExceptionLogger.cs
Expand Up @@ -30,7 +30,15 @@ internal class LogProviderExceptionLogger : IExceptionLogger

public async Task LogAsync(ExceptionLoggerContext context, CancellationToken cancellationToken)
{
Logger.ErrorException("Unhandled exception", context.Exception);
if(context.Request != null)
{
var mesage = string.Format("Unhandled exception accessing: {0}", context.Request.RequestUri.AbsolutePath);
Logger.ErrorException(mesage, context.Exception);
}
else
{
Logger.ErrorException("Unhandled exception", context.Exception);
}

var env = context.Request.GetOwinEnvironment();
var events = env.ResolveDependency<IEventService>();
Expand Down
10 changes: 9 additions & 1 deletion source/Core/Configuration/Hosting/MessageCookie.cs
Expand Up @@ -170,7 +170,15 @@ TMessage ReadByCookieName(string name)
var data = ctx.Request.Cookies[name];
if (!String.IsNullOrWhiteSpace(data))
{
return Unprotect(data);
try
{
return Unprotect(data);
}
catch(Exception ex)
{
Logger.WarnException("Error unprotecting cookie: {0}", ex, name);
ClearByCookieName(name);
}
}
return null;
}
Expand Down
2 changes: 2 additions & 0 deletions source/Core/Constants.cs
Expand Up @@ -599,6 +599,7 @@ public static class ClaimTypes
public static readonly string[] OidcProtocolClaimTypes = new string[]
{
ClaimTypes.Subject,
ClaimTypes.ClientId,
//ClaimTypes.Name,
ClaimTypes.AuthenticationMethod,
ClaimTypes.IdentityProvider,
Expand Down Expand Up @@ -740,6 +741,7 @@ public static class OwinEnvironment

public static class Authentication
{
public const string SigninQueryParamName = "signin";
public const string SigninId = "signinid";
public const string SignoutId = "id";
public const string KatanaAuthenticationType = "katanaAuthenticationType";
Expand Down
19 changes: 14 additions & 5 deletions source/Core/Endpoints/AuthenticationController.cs
Expand Up @@ -226,8 +226,8 @@ public async Task<IHttpActionResult> LoginLocal(string signin, LoginCredentials

var authenticationContext = new LocalAuthenticationContext
{
UserName = model.Username,
Password = model.Password,
UserName = model.Username.Trim(),
Password = model.Password.Trim(),
SignInMessage = signInMessage
};

Expand Down Expand Up @@ -672,7 +672,7 @@ private IHttpActionResult HandleNoSignin()

private async Task<IHttpActionResult> SignInAndRedirectAsync(SignInMessage signInMessage, string signInMessageId, AuthenticateResult authResult, bool? rememberMe = null)
{
var postAuthenActionResult = await PostAuthenticateAsync(signInMessage, authResult);
var postAuthenActionResult = await PostAuthenticateAsync(signInMessage, signInMessageId, authResult);
if (postAuthenActionResult != null)
{
if (postAuthenActionResult.Item1 != null)
Expand Down Expand Up @@ -705,7 +705,7 @@ private async Task<IHttpActionResult> SignInAndRedirectAsync(SignInMessage signI
return Redirect(redirectUrl);
}

private async Task<Tuple<IHttpActionResult, AuthenticateResult>> PostAuthenticateAsync(SignInMessage signInMessage, AuthenticateResult result)
private async Task<Tuple<IHttpActionResult, AuthenticateResult>> PostAuthenticateAsync(SignInMessage signInMessage, string signInMessageId, AuthenticateResult result)
{
if (result.IsPartialSignIn == false)
{
Expand All @@ -728,7 +728,16 @@ private async Task<IHttpActionResult> SignInAndRedirectAsync(SignInMessage signI
if (authResult.IsError)
{
Logger.WarnFormat("user service PostAuthenticateAsync returned an error message: {0}", authResult.ErrorMessage);
return new Tuple<IHttpActionResult, AuthenticateResult>(RenderErrorPage(authResult.ErrorMessage), null);
if (ctx.ShowLoginPageOnErrorResult)
{
Logger.Debug("ShowLoginPageOnErrorResult set to true, showing login page with error");
return new Tuple<IHttpActionResult, AuthenticateResult>(await RenderLoginPage(signInMessage, signInMessageId, authResult.ErrorMessage), null);
}
else
{
Logger.Debug("ShowLoginPageOnErrorResult set to false, showing error page with error");
return new Tuple<IHttpActionResult, AuthenticateResult>(RenderErrorPage(authResult.ErrorMessage), null);
}
}

if (result != authResult)
Expand Down
15 changes: 13 additions & 2 deletions source/Core/Endpoints/Connect/UserInfoEndpointController.cs
Expand Up @@ -95,10 +95,21 @@ public async Task<IHttpActionResult> GetUserInfo(HttpRequestMessage request)
}

// pass scopes/claims to profile service
var subject = tokenResult.Claims.FirstOrDefault(c => c.Type == Constants.ClaimTypes.Subject).Value;
var tokenClaims = tokenResult.Claims;
if (!tokenClaims.Any(x=>x.Type == Constants.ClaimTypes.Subject))
{
var error = "Token contains no sub claim";
Logger.Error(error);
await RaiseFailureEventAsync(error);
return Error(Constants.ProtectedResourceErrors.InvalidToken);
}


var userClaims = tokenClaims.Where(x => !Constants.OidcProtocolClaimTypes.Contains(x.Type) ||
Constants.AuthenticateResultClaimTypes.Contains(x.Type));
var scopes = tokenResult.Claims.Where(c => c.Type == Constants.ClaimTypes.Scope).Select(c => c.Value);

var payload = await _generator.ProcessAsync(subject, scopes, tokenResult.Client);
var payload = await _generator.ProcessAsync(userClaims, scopes, tokenResult.Client);

Logger.Info("End userinfo request");
await RaiseSuccessEventAsync();
Expand Down
10 changes: 8 additions & 2 deletions source/Core/Extensions/OwinEnvironmentExtensions.cs
Expand Up @@ -436,7 +436,7 @@ public static SignInMessage GetSignInMessage(this IDictionary<string, object> en
if (env == null) throw new ArgumentNullException("env");

var ctx = new OwinContext(env);
var id = ctx.Request.Query.Get(Constants.Authentication.SigninId);
var id = ctx.Request.Query.Get(Constants.Authentication.SigninQueryParamName);

if (String.IsNullOrWhiteSpace(id)) return null;

Expand Down Expand Up @@ -705,8 +705,9 @@ public static IEnumerable<string> GetClientIdsForCurrentAuthenticationSession(th
/// <param name="clientId">The value of the client_id claim in the token.</param>
/// <param name="scope">The value of the scope claim in the token.</param>
/// <param name="lifetime">The lifetime of the token.</param>
/// <param name="extraClaims">Additional claims to include in token.</param>
/// <returns>a JWT</returns>
public static async Task<string> IssueClientToken(this IDictionary<string, object> env, string clientId, string scope, int lifetime)
public static async Task<string> IssueClientToken(this IDictionary<string, object> env, string clientId, string scope, int lifetime, List<Claim> extraClaims = null)
{
var signingService = env.ResolveDependency<ITokenSigningService>();
var issuerUri = env.GetIdentityServerIssuerUri();
Expand All @@ -723,6 +724,11 @@ public static async Task<string> IssueClientToken(this IDictionary<string, objec
}
};

if (extraClaims != null)
{
token.Claims.AddRange(extraClaims);
}

return await signingService.SignTokenAsync(token);
}
}
Expand Down
5 changes: 5 additions & 0 deletions source/Core/Models/Contexts/PostAuthenticationContext.cs
Expand Up @@ -36,5 +36,10 @@ public class PostAuthenticationContext
/// The authenticate result.
/// </value>
public AuthenticateResult AuthenticateResult { get; set; }

/// <summary>
/// Gets or sets if the login page should be used to show the error from the authenticate result (as opposed to the general error page).
/// </summary>
public bool ShowLoginPageOnErrorResult { get; set; }
}
}
21 changes: 19 additions & 2 deletions source/Core/ResponseHandling/TokenResponseGenerator.cs
Expand Up @@ -144,7 +144,7 @@ private async Task<TokenResponse> ProcessRefreshTokenRequestAsync(ValidatedToken

var oldAccessToken = request.RefreshToken.AccessToken;
string accessTokenString;

// if pop request, claims must be updated because we need a fresh proof token
if (request.Client.UpdateAccessTokenClaimsOnRefresh || request.RequestedTokenType == RequestedTokenTypes.PoP)
{
Expand Down Expand Up @@ -191,6 +191,8 @@ private async Task<TokenResponse> ProcessRefreshTokenRequestAsync(ValidatedToken
response.Algorithm = request.ProofKeyAlgorithm;
}

response.IdentityToken = await CreateIdTokenFromRefreshTokenRequestAsync(request, accessTokenString);

return response;
}

Expand All @@ -202,7 +204,7 @@ private async Task<TokenResponse> ProcessRefreshTokenRequestAsync(ValidatedToken
if (request.AuthorizationCode != null)
{
createRefreshToken = request.AuthorizationCode.RequestedScopes.Select(s => s.Name).Contains(Constants.StandardScopes.OfflineAccess);

tokenRequest = new TokenCreationRequest
{
Subject = request.AuthorizationCode.Subject,
Expand Down Expand Up @@ -247,5 +249,20 @@ private string GetProofKey(ValidatedTokenRequest request)
// for now we only support client generated proof keys
return request.ProofKey;
}

private async Task<string> CreateIdTokenFromRefreshTokenRequestAsync(ValidatedTokenRequest request, string newAccessToken)
{
var oldAccessToken = request.RefreshToken.AccessToken;
var tokenRequest = new TokenCreationRequest
{
Subject = request.RefreshToken.GetOriginalSubject(),
Client = request.Client,
Scopes = await _scopes.FindScopesAsync(oldAccessToken.Scopes),
ValidatedRequest = request,
AccessTokenToHash = newAccessToken
};
var idToken = await _tokenService.CreateIdentityTokenAsync(tokenRequest);
return await _tokenService.CreateSecurityTokenAsync(idToken);
}
}
}
10 changes: 6 additions & 4 deletions source/Core/ResponseHandling/UserInfoResponseGenerator.cs
Expand Up @@ -23,6 +23,7 @@
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using System;

namespace IdentityServer3.Core.ResponseHandling
{
Expand All @@ -38,15 +39,16 @@ public UserInfoResponseGenerator(IUserService users, IScopeStore scopes)
_scopes = scopes;
}

public async Task<Dictionary<string, object>> ProcessAsync(string subject, IEnumerable<string> scopes, Client client)
public async Task<Dictionary<string, object>> ProcessAsync(IEnumerable<Claim> userClaims, IEnumerable<string> scopes, Client client)
{
Logger.Info("Creating userinfo response");
var profileData = new Dictionary<string, object>();

var requestedClaimTypes = await GetRequestedClaimTypesAsync(scopes);
var principal = Principal.Create("UserInfo", new Claim("sub", subject));

var principal = Principal.Create("UserInfo", userClaims.ToArray());

IEnumerable<Claim> profileClaims;

var requestedClaimTypes = await GetRequestedClaimTypesAsync(scopes);
if (requestedClaimTypes.IncludeAllClaims)
{
Logger.InfoFormat("Requested claim types: all");
Expand Down
2 changes: 1 addition & 1 deletion source/Core/Validation/TokenRequestValidator.cs
Expand Up @@ -508,7 +508,7 @@ private async Task<TokenRequestValidationResult> ValidateResourceOwnerCredential
{
error = "Partial signin returned from AuthenticateLocalAsync";
}
LogError("User authentication failed: " + error);
LogWarn("User authentication failed: " + error);
await RaiseFailedResourceOwnerAuthenticationEventAsync(userName, signInMessage, error);

if (authnResult != null)
Expand Down
4 changes: 2 additions & 2 deletions source/Core/Validation/TokenValidator.cs
Expand Up @@ -331,8 +331,8 @@ private IEnumerable<Claim> ReferenceTokenToClaims(Token token)
{
new Claim(Constants.ClaimTypes.Audience, token.Audience),
new Claim(Constants.ClaimTypes.Issuer, token.Issuer),
new Claim(Constants.ClaimTypes.NotBefore, token.CreationTime.ToEpochTime().ToString()),
new Claim(Constants.ClaimTypes.Expiration, token.CreationTime.AddSeconds(token.Lifetime).ToEpochTime().ToString())
new Claim(Constants.ClaimTypes.NotBefore, token.CreationTime.ToEpochTime().ToString(), ClaimValueTypes.Integer),
new Claim(Constants.ClaimTypes.Expiration, token.CreationTime.AddSeconds(token.Lifetime).ToEpochTime().ToString(), ClaimValueTypes.Integer)
};

claims.AddRange(token.Claims);
Expand Down
16 changes: 16 additions & 0 deletions source/Tests/UnitTests/Endpoints/AuthenticationControllerTests.cs
Expand Up @@ -727,6 +727,22 @@ public void PostToLogin_PostAuthenticate_returns_error_and_error_page_is_rendere
cookies.Count(x => x.StartsWith(Constants.PrimaryAuthenticationType + "=")).Should().Be(0);
}

[Fact]
public void PostToLogin_PostAuthenticateReturnsErrorAndShowLoginPageOnErrorResultIsSet_ShowsLoginPageWithError()
{
mockUserService.OnPostAuthenticate = ctx => {
ctx.AuthenticateResult = new AuthenticateResult("SomeError");
ctx.ShowLoginPageOnErrorResult = true;
return Task.FromResult(0);
};

GetLoginPage();
var resp = PostForm(GetLoginUrl(), new LoginCredentials { Username = "alice", Password = "alice" });
resp.AssertPage("login");
var model = resp.GetModel<LoginViewModel>();
model.ErrorMessage.Should().Be("SomeError");
}

[Fact]
public void PostToLogin_PostAuthenticate_returns_partial_login_and_user_is_not_logged_in()
{
Expand Down
Binary file modified source/VersionAssemblyInfo.cs
Binary file not shown.

0 comments on commit ad4fdcc

Please sign in to comment.