Skip to content

Commit

Permalink
Complete User Info End Poing
Browse files Browse the repository at this point in the history
  • Loading branch information
Shoogn committed Mar 2, 2024
1 parent 3e16a00 commit 3312f95
Show file tree
Hide file tree
Showing 11 changed files with 69 additions and 29 deletions.
2 changes: 1 addition & 1 deletion Sample/ClientApp_OpenId/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
options.CallbackPath = "/signin-oidc";
options.SaveTokens = true;
options.Scope.Add("jwtapitestapp.read");
// options.GetClaimsFromUserInfoEndpoint = true;
options.GetClaimsFromUserInfoEndpoint = true;
});


Expand Down
2 changes: 1 addition & 1 deletion Server/src/OAuth20.Server/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public static class ContentTypeSupported

public static class AuthenticatedRequestScheme
{
public const string AuthorizationRequestHeader = "Beraer";
public const string AuthorizationRequestHeader = "Bearer";
public const string FormEncodedBodyParameter = "access_token";
public const string UriQueryParameter = "access_token";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public JsonResult GetConfiguration()
acr_values_supported = new string[] {"urn:mace:incommon:iap:silver", "urn:mace:incommon:iap:bronze"},
response_types_supported = new string[] { "code", "code id_token", "id_token", "token id_token" },
subject_types_supported = new string[] { "public", "pairwise" },
user_info_endpoint = "https://localhost:7275/UserInfo/GetUserInfo",
userinfo_endpoint = "https://localhost:7275/api/UserInfo/GetUserInfo",
userinfo_encryption_enc_values_supported = new string[] { "A128CBC-HS256", "A128GCM" },
id_token_signing_alg_values_supported = new string[] { "RS256", "ES256", "HS256" , "SHA256" },
id_token_encryption_alg_values_supported = new string[] { "RSA1_5", "A128KW" },
Expand Down
10 changes: 8 additions & 2 deletions Server/src/OAuth20.Server/Controllers/UserInfoController.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
using OAuth20.Server.Services;
using System.Threading.Tasks;

namespace OAuth20.Server.Controllers
{
[Route("api/[controller]")]
[EnableCors("UserInfoPolicy")]
[ApiController]
[AllowAnonymous]
public class UserInfoController : ControllerBase
{
private readonly IUserInfoService _userInfoService;
public UserInfoController(IUserInfoService userInfoService)
{
_userInfoService = userInfoService;
}
[HttpGet, HttpPost]

[HttpGet("GetUserInfo")]

public async Task<IActionResult> GetUserInfo()
{
var userInfo = await _userInfoService.GetUserInfoAsync();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public class DiscoveryResponse
public string token_endpoint { get; set; }
public IList<string> token_endpoint_auth_methods_supported { get; set; }
public IList<string> token_endpoint_auth_signing_alg_values_supported { get; set; }
public string user_info_endpoint { get; set; }
public string userinfo_endpoint { get; set; }
public string check_session_iframe { get; set; }
public string end_session_endpoint { get; set; }
public string jwks_uri { get; set; }
Expand Down
19 changes: 15 additions & 4 deletions Server/src/OAuth20.Server/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,16 +66,27 @@ Everyone is permitted to copy and distribute verbatim copies
builder.Services.AddScoped<IBearerTokenUsageTypeValidation, BearerTokenUsageTypeValidation>();
builder.Services.AddHttpContextAccessor();

builder.Services.Configure<RouteOptions>(options =>
//builder.Services.Configure<RouteOptions>(options =>
//{
// options.LowercaseQueryStrings = true;
// options.LowercaseUrls = true;
//});

builder.Services.AddCors(options =>
{
options.LowercaseQueryStrings = true;
options.LowercaseUrls = true;
});
options.AddPolicy("UserInfoPolicy", o =>
{
o.AllowAnyOrigin();
o.AllowAnyHeader();
o.AllowAnyMethod();
});
});
builder.Services.AddControllersWithViews();
var app = builder.Build();

app.UseStaticFiles();
app.UseRouting();
app.UseCors("UserInfoPolicy");
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
Expand Down
31 changes: 18 additions & 13 deletions Server/src/OAuth20.Server/Services/AuthorizeResultService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ public AuthorizeResponse AuthorizeRequest(IHttpContextAccessor httpContextAccess


// check the return url is match the one that in the client store
bool redirectUriIsMatched = client.Client.RedirectUri.Equals(authorizationRequest.redirect_uri,StringComparison.OrdinalIgnoreCase);
if(!redirectUriIsMatched)
bool redirectUriIsMatched = client.Client.RedirectUri.Equals(authorizationRequest.redirect_uri, StringComparison.OrdinalIgnoreCase);
if (!redirectUriIsMatched)
{
response.Error = ErrorTypeEnum.InvalidRequest.GetEnumDescription();
response.ErrorDescription = "redirect uri is not matched the one in the client store";
Expand Down Expand Up @@ -164,7 +164,7 @@ public TokenResponse GenerateToken(TokenRequest tokenRequest)
}
IEnumerable<string> scopes = checkClientResult.Client.AllowedScopes.Intersect(tokenRequest.scope);

var clientCredentialAccessTokenResult = generateJWTTokne(scopes, Constants.TokenTypes.JWTAcceseccToken, checkClientResult.Client);
var clientCredentialAccessTokenResult = generateJWTTokne(scopes, Constants.TokenTypes.JWTAcceseccToken, checkClientResult.Client, null);
SaveJWTTokenInBackStore(checkClientResult.Client.ClientId, clientCredentialAccessTokenResult.AccessToken, clientCredentialAccessTokenResult.ExpirationDate);

result.access_token = clientCredentialAccessTokenResult.AccessToken;
Expand Down Expand Up @@ -200,6 +200,7 @@ public TokenResponse GenerateToken(TokenRequest tokenRequest)


string id_token = string.Empty;
string userId = null;
if (clientCodeChecker.IsOpenId)
{
if (!clientCodeChecker.Subject.Identity.IsAuthenticated)
Expand All @@ -208,7 +209,7 @@ public TokenResponse GenerateToken(TokenRequest tokenRequest)

var currentUserName = clientCodeChecker.Subject.Identity.Name;

var userId = clientCodeChecker.Subject.Claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier)?.Value;
userId = clientCodeChecker.Subject.Claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier)?.Value;

if (string.IsNullOrEmpty(userId) || string.IsNullOrEmpty(currentUserName))
return new TokenResponse { Error = ErrorTypeEnum.InvalidGrant.GetEnumDescription() };
Expand All @@ -226,10 +227,10 @@ public TokenResponse GenerateToken(TokenRequest tokenRequest)

var claims = new List<Claim>()
{
new Claim("sub", userId),
new Claim("given_name", currentUserName),
new Claim("sub", userId, ClaimValueTypes.String),
new Claim("given_name", currentUserName, ClaimValueTypes.String),
new Claim("iat", iat.ToString(), ClaimValueTypes.Integer), // time stamp
new Claim("nonce", clientCodeChecker.Nonce)
new Claim("nonce", clientCodeChecker.Nonce, ClaimValueTypes.String)
};
foreach (var amr in amrs)
claims.Add(new Claim("amr", amr));// authentication
Expand All @@ -239,7 +240,6 @@ public TokenResponse GenerateToken(TokenRequest tokenRequest)
var token = new JwtSecurityToken(_options.IDPUri, checkClientResult.Client.ClientId, claims,
expires: DateTime.UtcNow.AddMinutes(int.Parse("50")), signingCredentials: new
SigningCredentials(rsaSecurityKey, SecurityAlgorithms.RsaSha256));

id_token = handler.WriteToken(token);

var idptoken = new OAuthTokenEntity
Expand All @@ -263,7 +263,7 @@ public TokenResponse GenerateToken(TokenRequest tokenRequest)
where !OAuth2ServerHelpers.OpenIdConnectScopes.Contains(m)
select m;

var accessTokenResult = generateJWTTokne(scopesinJWtAccessToken, Constants.TokenTypes.JWTAcceseccToken, checkClientResult.Client);
var accessTokenResult = generateJWTTokne(scopesinJWtAccessToken, Constants.TokenTypes.JWTAcceseccToken, checkClientResult.Client, userId);
SaveJWTTokenInBackStore(checkClientResult.Client.ClientId, accessTokenResult.AccessToken, accessTokenResult.ExpirationDate);

// here remove the code from the Concurrent Dictionary
Expand Down Expand Up @@ -314,20 +314,25 @@ private bool codeVerifierIsSendByTheClientThatReceivedTheCode(string codeVerifie
}





public TokenResult generateJWTTokne(IEnumerable<string> scopes, string tokenType, Client client)

public TokenResult generateJWTTokne(IEnumerable<string> scopes, string tokenType, Client client, string sub)
{
var result = new TokenResult();

if (tokenType == Constants.TokenTypes.JWTAcceseccToken)
{
var claims_at = new List<Claim>
var claims = new List<Claim>
{
new Claim("scope", string.Join(' ', scopes))
};

if (!string.IsNullOrEmpty(sub))
{
claims.Add(new Claim("sub", sub, ClaimValueTypes.String));
}

RSACryptoServiceProvider provider1 = new RSACryptoServiceProvider();

string publicPrivateKey1 = File.ReadAllText("PublicPrivateKey.xml");
Expand All @@ -336,7 +341,7 @@ public TokenResult generateJWTTokne(IEnumerable<string> scopes, string tokenType
RsaSecurityKey rsaSecurityKey1 = new RsaSecurityKey(provider1);
JwtSecurityTokenHandler handler1 = new JwtSecurityTokenHandler();

var token1 = new JwtSecurityToken(_options.IDPUri, client.ClientUri, claims_at, notBefore: DateTime.UtcNow,
var token1 = new JwtSecurityToken(_options.IDPUri, client.ClientUri, claims, notBefore: DateTime.UtcNow,
expires: DateTime.UtcNow.AddMinutes(int.Parse("50")), signingCredentials: new
SigningCredentials(rsaSecurityKey1, SecurityAlgorithms.RsaSha256));

Expand Down
14 changes: 14 additions & 0 deletions Server/src/OAuth20.Server/Services/IClientService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ CheckClientResult VerifyClientById(string clientId, bool checkWithSecret = false
AudienceValidator ValidateAudienceHandler(IEnumerable<string> audiences, SecurityToken securityToken,
TokenValidationParameters validationParameters, Client client, string token);

Task<CheckClientResult> GetClientByUriAsync(string clientUrl);

Task<CheckClientResult> GetClientByIdAsync(string clientId);
}

Expand Down Expand Up @@ -66,6 +68,18 @@ public Task<CheckClientResult> GetClientByIdAsync(string clientId)
//}
}


public Task<CheckClientResult> GetClientByUriAsync(string clientUrl)
{
var c = _clientStore.Clients.Where(x => x.ClientUri == clientUrl).FirstOrDefault();
var response = new CheckClientResult
{
Client = c,
IsSuccess = true
};
return Task.FromResult(response);
}

public CheckClientResult VerifyClientById(string clientId, bool checkWithSecret = false, string clientSecret = null,
string grantType = null)
{
Expand Down
12 changes: 8 additions & 4 deletions Server/src/OAuth20.Server/Services/UserInfoService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Everyone is permitted to copy and distribute verbatim copies
using System.IdentityModel.Tokens.Jwt;
using System.IO;
using System.Linq;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Threading.Tasks;

Expand Down Expand Up @@ -75,8 +76,10 @@ public async Task<UserInfoResponse> GetUserInfoAsync()
JwtSecurityTokenHandler jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
JwtSecurityToken jwtSecurityToken = jwtSecurityTokenHandler.ReadJwtToken(bearerTokenUsages.Token);

var clientId = jwtSecurityToken.Audiences.FirstOrDefault();
var client = await _clientService.GetClientByIdAsync(clientId);
var aud = jwtSecurityToken.Audiences.FirstOrDefault();
var client = await _clientService.GetClientByUriAsync(aud);



// TODO:
// check if client is null.
Expand All @@ -100,8 +103,9 @@ public async Task<UserInfoResponse> GetUserInfoAsync()

if (tokenValidationReslt.IsValid)
{
string userId = tokenValidationReslt.ClaimsIdentity.FindFirst("sub")?.Value;

var payload = jwtSecurityToken.Payload;
//string userId = tokenValidationReslt.ClaimsIdentity.FindFirst("sub")?.Value;
var userId = payload.Claims.FirstOrDefault(x => x.Type == "sub")?.Value;
// TODO:
// check userId is null

Expand Down
2 changes: 1 addition & 1 deletion Server/src/OAuth20.Server/appsettings.Development.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"BaseDBConnection": "Data Source=DEV01\\SQLEXPRESS;Initial Catalog=OAuth20_Db;Trusted_Connection=True;MultipleActiveResultSets=true"
},
"AllowedHosts": "*",
"OAuthOption": {
"OAuthOptions": {
"Provider": "InMemory",
"IsAvaliable": true,
"IDPUri": "https://localhost:7275"
Expand Down
2 changes: 1 addition & 1 deletion Server/src/OAuth20.Server/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"BaseDBConnection": "Data Source=DEV01\\SQLEXPRESS;Initial Catalog=OAuth20_Db;Trusted_Connection=True;MultipleActiveResultSets=true"
},
"AllowedHosts": "*",
"OAuthOption": {
"OAuthOptions": {
"Provider": "InMemory",
"IsAvaliable": true,
"IDPUri": "https://localhost:7275"
Expand Down

0 comments on commit 3312f95

Please sign in to comment.