Skip to content

Commit 964f294

Browse files
API: provide login invalid credentials problemdetails
1 parent dddce01 commit 964f294

File tree

4 files changed

+93
-4
lines changed

4 files changed

+93
-4
lines changed
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text.Json.Serialization;
4+
5+
namespace Certify.Models.Util
6+
{
7+
// extracted from the following to avoid including MVC dependencies
8+
//https://github.com/dotnet/aspnetcore/blob/main/src/Http/Http.Abstractions/src/ProblemDetails/ProblemDetails.cs
9+
// Licensed to the .NET Foundation under one or more agreements.
10+
// The .NET Foundation licenses this file to you under the MIT license.
11+
12+
/// <summary>
13+
/// A machine-readable format for specifying errors in HTTP API responses based on <see href="https://tools.ietf.org/html/rfc7807"/>.
14+
/// </summary>
15+
public class ProblemDetails
16+
{
17+
/// <summary>
18+
/// A URI reference [RFC3986] that identifies the problem type. This specification encourages that, when
19+
/// dereferenced, it provide human-readable documentation for the problem type
20+
/// (e.g., using HTML [W3C.REC-html5-20141028]). When this member is not present, its value is assumed to be
21+
/// "about:blank".
22+
/// </summary>
23+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
24+
[JsonPropertyOrder(-5)]
25+
[JsonPropertyName("type")]
26+
public string? Type { get; set; }
27+
28+
/// <summary>
29+
/// A short, human-readable summary of the problem type. It SHOULD NOT change from occurrence to occurrence
30+
/// of the problem, except for purposes of localization(e.g., using proactive content negotiation;
31+
/// see[RFC7231], Section 3.4).
32+
/// </summary>
33+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
34+
[JsonPropertyOrder(-4)]
35+
[JsonPropertyName("title")]
36+
public string? Title { get; set; }
37+
38+
/// <summary>
39+
/// The HTTP status code([RFC7231], Section 6) generated by the origin server for this occurrence of the problem.
40+
/// </summary>
41+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
42+
[JsonPropertyOrder(-3)]
43+
[JsonPropertyName("status")]
44+
public int? Status { get; set; }
45+
46+
/// <summary>
47+
/// A human-readable explanation specific to this occurrence of the problem.
48+
/// </summary>
49+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
50+
[JsonPropertyOrder(-2)]
51+
[JsonPropertyName("detail")]
52+
public string? Detail { get; set; }
53+
54+
/// <summary>
55+
/// A URI reference that identifies the specific occurrence of the problem. It may or may not yield further information if dereferenced.
56+
/// </summary>
57+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
58+
[JsonPropertyOrder(-1)]
59+
[JsonPropertyName("instance")]
60+
public string? Instance { get; set; }
61+
62+
/// <summary>
63+
/// Gets the <see cref="IDictionary{TKey, TValue}"/> for extension members.
64+
/// <para>
65+
/// Problem type definitions MAY extend the problem details object with additional members. Extension members appear in the same namespace as
66+
/// other members of a problem type.
67+
/// </para>
68+
/// </summary>
69+
/// <remarks>
70+
/// The round-tripping behavior for <see cref="Extensions"/> is determined by the implementation of the Input \ Output formatters.
71+
/// In particular, complex types or collection types may not round-trip to the original type when using the built-in JSON or XML formatters.
72+
/// </remarks>
73+
[JsonExtensionData]
74+
public IDictionary<string, object?> Extensions { get; set; } = new Dictionary<string, object?>(StringComparer.Ordinal);
75+
}
76+
}

src/Certify.Server/Certify.Server.Hub.Api.Client/Certify.Server.Hub.Api.Client.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
using Certify.Models.Hub;
1313
using Certify.Models.Config.Migration;
1414
using Certify.Shared;
15+
using ProblemDetails=Certify.Models.Util.ProblemDetails;
1516

1617
#pragma warning disable 108 // Disable "CS0108 '{derivedDto}.ToJson()' hides inherited member '{dtoBase}.ToJson()'. Use the new keyword if hiding was intended."
1718
#pragma warning disable 114 // Disable "CS0114 '{derivedDto}.RaisePropertyChanged(String)' hides inherited member 'dtoBase.RaisePropertyChanged(String)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword."
@@ -1456,6 +1457,16 @@ public virtual async System.Threading.Tasks.Task<AuthResponse> LoginAsync(AuthRe
14561457
return objectResponse_.Object;
14571458
}
14581459
else
1460+
if (status_ == 401)
1461+
{
1462+
var objectResponse_ = await ReadObjectResponseAsync<ProblemDetails>(response_, headers_, cancellationToken).ConfigureAwait(false);
1463+
if (objectResponse_.Object == null)
1464+
{
1465+
throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
1466+
}
1467+
throw new ApiException<ProblemDetails>("Unauthorized", status_, objectResponse_.Text, headers_, objectResponse_.Object, null);
1468+
}
1469+
else
14591470
{
14601471
var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
14611472
throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);

src/Certify.Server/Certify.Server.Hub.Api.Client/nswag.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@
5454
"Certify.Models.Providers",
5555
"Certify.Models.Hub",
5656
"Certify.Models.Config.Migration",
57-
"Certify.Shared"
57+
"Certify.Shared",
58+
"ProblemDetails=Certify.Models.Util.ProblemDetails"
5859
],
5960
"additionalContractNamespaceUsages": [],
6061
"generateOptionalParameters": false,

src/Certify.Server/Certify.Server.Hub.Api/Controllers/v1/AuthController.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ public async Task<IActionResult> CheckAuthStatus()
5151
/// <returns>Response contains access token and refresh token for API operations.</returns>
5252
[HttpPost]
5353
[Route("login")]
54-
[ProducesResponseType(typeof(AuthResponse), 200)]
54+
[ProducesResponseType(typeof(AuthResponse), StatusCodes.Status200OK)]
55+
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status401Unauthorized)]
5556
public async Task<IActionResult> Login(AuthRequest login)
5657
{
5758

@@ -79,7 +80,7 @@ public async Task<IActionResult> Login(AuthRequest login)
7980
}
8081
else
8182
{
82-
return Unauthorized();
83+
return Unauthorized(new ProblemDetails { Status = StatusCodes.Status401Unauthorized, Title = "Invalid username or password" });
8384
}
8485
}
8586

@@ -91,7 +92,7 @@ public async Task<IActionResult> Login(AuthRequest login)
9192
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
9293
[HttpPost]
9394
[Route("refresh")]
94-
[ProducesResponseType(typeof(AuthResponse), 200)]
95+
[ProducesResponseType(typeof(AuthResponse), StatusCodes.Status200OK)]
9596
public async Task<IActionResult> Refresh(string refreshToken)
9697
{
9798
var authToken = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]!).Parameter;

0 commit comments

Comments
 (0)