I'm currently struggling with OpenIdentityServer 4 in ASP Core 1.1.
I'm able to grant tokens, using ResourceOwnerPassword grant type etc. Created my custom ResourcePasswordValidator etc.
Currently in my test application I retrieve a token with user credentials and all issues fine, however when I try to access the IdentityController with an [Authorize] attribute I'm redirected to unauthorized page and sent a 403 forbidden http code
I'm not sure what the issue is. I suspect it could be from scope/resource issue
Any help whatsoever appreciated.
Sample code for consumer
public class TestAuthentication
{
private HttpClient _client;
public TestAuthentication()
{
_client = new HttpClient();
}
public async Task RunTest()
{
var token = await GetToken();
if (string.IsNullOrWhiteSpace(token)) return;
await GetClaims(token);
}
private async Task<string> GetToken()
{
var response = "";
var disco = await DiscoveryClient.GetAsync("http://localhost:5000");
//var tokenClient = new TokenClient(disco.TokenEndpoint, "EduOne", "secret");
//var tokenResponse = await tokenClient.RequestClientCredentialsAsync("api");
var tokenClient = new TokenClient(disco.TokenEndpoint, "ro.client", "secret");
var tokenResponse = await tokenClient.RequestResourceOwnerPasswordAsync("alice#mail.com", "Password1!", "api1");
// var tokenResponse = await tokenClient.RequestResourceOwnerPasswordAsync("alice#mail.com", "Password1!", "openid");
if (tokenResponse.IsError)
{
Console.Out.WriteLine("Error:");
Console.Out.WriteLine(tokenResponse.Error);
Console.Out.Write(tokenResponse.ErrorDescription);
}
else
{
var extraClaims = new UserInfoClient(disco.UserInfoEndpoint);
var identityClaims = await extraClaims.GetAsync(tokenResponse.AccessToken);
response = tokenResponse.Json.ToString();
Console.Out.WriteLine($"token: {response}");
}
return response;
}
private async Task GetClaims(string token)
{
try
{
var obj = JObject.Parse(token);
var tok = obj["access_token"]?.ToString();
_client = new HttpClient();
_client.SetBearerToken(tok);
var response = await _client.GetAsync("http://localhost:5000/api/v1/identity");
if (!response.IsSuccessStatusCode)
{
Console.WriteLine(response.StatusCode);
}
else
{
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine(JArray.Parse(content));
}
}
catch (Exception e)
{
var m = e.Message;
//throw;
}
}
~TestAuthentication()
{
_client = null;
}
}
Code for setups:
Client =>
new Client
{
ClientId = "ro.client",
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
ClientSecrets =
{
new Secret("secret".Sha256())
},
AllowedScopes = {"api1" },
AccessTokenType = AccessTokenType.Reference
},
User =>
new TestUser
{
SubjectId = "1",
Username = "alice#mail.com",
Password = "Password1!",
Claims =
{
new Claim(JwtClaimTypes.Email, "mail#mail.com")
}
},
Resource =>
new IdentityResource("api1", new string[]{JwtClaimTypes.Email})
Startup =>
app.UseIdentityServer();
// app.UseIdentity();
// app.UseIdentity();
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
ApiSecret = "secret",
Authority = "http://localhost:5000",
RequireHttpsMetadata = false,
DiscoveryDocumentRefreshInterval = TimeSpan.FromMinutes(5),
ApiName = "FiserOpenIdentityApi",
SupportedTokens = IdentityServer4.AccessTokenValidation.SupportedTokens.Both,
AllowedScopes = { "openid", "profile", "email", "api1", "FiserOpenIdentityApi" }
});
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationScheme = "Cookies"
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
AuthenticationScheme = "oidc",
SignInScheme = "Cookies",
Authority = "http://localhost:5000",
ClientId = "ro.client",
RequireHttpsMetadata = false,
ClientSecret = "secret",
SaveTokens = false
});
// app.UseJwtBearerAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapRoute(
name: "RESTApiV1",
template: "api/v1/{controller}/{action}/{id?}");
});
app.UseMongoDbForIdentityServer();
I am using Hybrid flow. i think you are missing authenticationHeader in HttpClient.
Please follow below code, its working and it may help you.
var client = new HttpClient();
var accessToken = await HttpContext
.GetTokenAsync(OpenIdConnectParameterNames.AccessToken);
var disco = await client.GetDiscoveryDocumentAsync("https://localhost:44323");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var response = await client.GetUserInfoAsync(new UserInfoRequest
{
Address = disco.UserInfoEndpoint,
Token = accessToken
});
var address = response.Claims.FirstOrDefault(c => c.Type == "address")?.Value;
var add = new AddressViewModel();
add.Address = address;
return View(add);
Related
We are working on an idetityserver4 (A SPA application in angular) that will run on a standalone server and will comunicate with an API(asp.net API) that is on another server, the patern we are trying to implement is BFF (backend for front end) and if we didn't misunderstand the concept badly, our ID4 will act as the gateway to the API, firstly we log to the ID4 with the ClientID and secret so that we get a token and generate the session cookies, afterward we forward everything to the API to complete the local login.
Everything works fine till here, we get the response we want, token gets set to acces the API and cookies generated for that client are automatically returned in Header (we use POSTMAN), but when i try to make a request to the API (request that firstly goes to ID4 to get verified if the client cookie is not expired) apparently ID4 does not verify if the client cookie is or not expired, i can make as many requests as i like....
We really don't understand how to do it properly and we didn't find something that can help us...
Here are the snippets
Startup.cs
services.AddAuthentication(options =>
{
options.DefaultScheme = "cookies";
options.DefaultChallengeScheme = "oidc";
options.RequireAuthenticatedSignIn = true;
})
.AddCookie("cookies", options =>
{
options.Cookie.Name = "cookie-bff";
options.Cookie.SameSite = SameSiteMode.Strict;
options.Cookie.HttpOnly = true;
options.ExpireTimeSpan = TimeSpan.FromMinutes(1);
options.Cookie.SecurePolicy = 0;
options.SlidingExpiration = true;
options.Events.OnSigningOut = async e =>
{
// revoke refresh token on sign-out
await e.HttpContext.RevokeUserRefreshTokenAsync();
};
})
.AddOpenIdConnect("oidc", options =>
{
options.Authority = "https://localhost:4001/";
options.ClientId = "angular-client";
options.ClientSecret = "secret";
options.ResponseType = "code";
options.GetClaimsFromUserInfoEndpoint = true;
options.SaveTokens = true;
/*
options.Scope.Clear();
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("api");
options.Scope.Add("offline_access");
*/
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = "role"
};
});
services.AddAccessTokenManagement(options =>
{
// client config is inferred from OpenID Connect settings
// if you want to specify scopes explicitly, do it here, otherwise the scope parameter will not be sent
options.Client.Scope = "write";
})
.ConfigureBackchannelHttpClient();
services.AddUserAccessTokenClient("user_client", client =>
{
client.BaseAddress = new Uri("https://localhost:5001/api/");
});
services.AddClientAccessTokenClient("client", configureClient: client =>
{
client.BaseAddress = new Uri("https://localhost:5001/api/");
});
Where we configure the Context for operation and configuration
services.AddIdentityServer(options => {
options.Authentication.CheckSessionCookieName = "cookie-bff";
options.Authentication.CookieLifetime = TimeSpan.FromMinutes(1);
options.Authentication.CookieSameSiteMode = SameSiteMode.Strict;
options.Authentication.CookieSlidingExpiration = true;
options.LowerCaseIssuerUri = false;
options.EmitScopesAsSpaceDelimitedStringInJwt = false;
})
.AddDeveloperSigningCredential()
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = b => b.UseSqlServer(configuration.GetConnectionString("LocalDevelopment"),
sql => sql.MigrationsAssembly(migrationAssembly));
})
.AddOperationalStore(options =>
{
options.ConfigureDbContext = b => b.UseSqlServer(configuration.GetConnectionString("LocalDevelopment"),
sql => sql.MigrationsAssembly(migrationAssembly));
});
This is our LoginController(its work in progres, we are beginers)
[HttpPost]
[Route("/identityserver/login")]
public async Task<IActionResult> Post([Required, FromBody] LoginPostDTO Json)
{
var client = new HttpClient();
var response = await client.RequestTokenAsync(new TokenRequest
{
Address = "https://localhost:4001/connect/token",
GrantType = "client_credentials",
ClientId = "angular-client",
ClientSecret = "secret",
Parameters =
{
{ "scope", "read"},
{"openid","profile"}
}
});
var claims = new List<Claim>
{
new Claim(JwtClaimTypes.Name,"Company"),
new Claim(JwtClaimTypes.Role, "Administrator"),
new Claim(JwtClaimTypes.Subject, "Company")
};
var claimsIdentity = new ClaimsIdentity(claims, "cookies");
var authProperties = new AuthenticationProperties
{
AllowRefresh = true,
// Refreshing the authentication session should be allowed.
ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(1),
// The time at which the authentication ticket expires. A
// value set here overrides the ExpireTimeSpan option of
// CookieAuthenticationOptions set with AddCookie.
IsPersistent = false,
// Whether the authentication session is persisted across
// multiple requests. When used with cookies, controls
// whether the cookie's lifetime is absolute (matching the
// lifetime of the authentication ticket) or session-based.
IssuedUtc = DateTime.Now,
// The time at which the authentication ticket was issued.
RedirectUri = "https://localhost:4001/signin-oidc"
// The full path or absolute URI to be used as an http
// redirect response value.
};
await HttpContext.SignInAsync("cookies", new ClaimsPrincipal(claimsIdentity), authProperties);
var stringContent = new StringContent(JsonConvert.SerializeObject(Json), Encoding.UTF8, "application/json");
var api = new HttpClient();
api.SetBearerToken(response.AccessToken);
var apiResponse = await api.PostAsync("https://localhost:5001/api/login", stringContent);
return Ok(apiResponse.Content.ReadAsAsync(typeof(JObject)).Result);
}
}
And here is the Controller where we comunicate with the API after we login
public class Controller : ControllerBase
{
[HttpPost]
[Route("/identityserver/request")]
[Produces("application/json")]
[Consumes("application/json")]
public async Task<IActionResult> Post([Required, FromBody]DTO Json)
{
var stringContent = new StringContent(JsonConvert.SerializeObject(Json), Encoding.UTF8, "application/json");
var api = new HttpClient();
api.SetBearerToken(await HttpContext.GetClientAccessTokenAsync());
var apiResponse = await api.PostAsync("https://localhost:5001/api/request/medical-request", stringContent);
return Ok(apiResponse.Content.ReadAsAsync(typeof(JObject)).Result);
}
}
Thank you in advance!
P.S This is my first post on stack :)
I would like to read e-mails from a signed in user using IMAP.
I have created a console application for testing purposes and making sure the app-registration settings in azure are correct.
The console application is working as intended.
A Microsoft login window is shown where the user can enter their credentials.
An access token is received and is passed to MailKit in order to get the user's emails.
The problem
When I try to authenticate using MailKit in a MVC .net standard web-application, I get an error saying "Authentication failed".
However, when I copy the access-token I acquired using the console- application and use it in my web-application I do not get the authorization error and can successfully authenticate and read emails. (I use the access-token as second parameter in
var oauth2 = new SaslMechanismOAuth2("[Email here]", oathToken.access_token);).
I have used DotNetOpenAuth in order to show a Microsoft login window.
(I could not find a MSAL example for a web-application where i didn't have to add OWIN as middleware. I only want to authenticate in order to get emails, not for application wide authentication and authorization.)
Console application code (this works):
// Using Microsoft.Identity.Client 4.22.0
// Configure the MSAL client to get tokens
var pcaOptions = new PublicClientApplicationOptions
{
ClientId = "[client-id here]",
AadAuthorityAudience = AadAuthorityAudience.AzureAdMultipleOrgs,
};
var pca = PublicClientApplicationBuilder
.CreateWithApplicationOptions(pcaOptions).Build();
var scopes = new string[] {
"email",
"offline_access",
"https://outlook.office365.com/IMAP.AccessAsUser.All" };
// Make the interactive token request
var authResult = await pca.AcquireTokenInteractive(scopes).ExecuteAsync();
var oauth2 = new SaslMechanismOAuth2(authResult.Account.Username, authResult.AccessToken);
using (var client = new ImapClient())
{
await client.ConnectAsync("outlook.office365.com", 993, SecureSocketOptions.Auto);
await client.AuthenticateAsync(oauth2);
var inbox = client.Inbox;
inbox.Open(FolderAccess.ReadOnly);
for (int i = 0; i < inbox.Count; i++)
{
var message = inbox.GetMessage(i);
Console.WriteLine("Subject: {0}", message.Subject);
}
await client.DisconnectAsync(true);
}
Web-application (this doesn't work):
public ActionResult Index()
{
string clientID = "[client-id here]";
string clientSecret = "[client-secret here]";
string redirectUri = "[redirectUri here]";
AuthorizationServerDescription server = new AuthorizationServerDescription
{
AuthorizationEndpoint = new Uri("https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize"),
TokenEndpoint = new Uri("https://login.microsoftonline.com/organizations/oauth2/v2.0/token"),
ProtocolVersion = ProtocolVersion.V20,
};
List<string> scopes = new List<string>
{
"email",
"offline_access",
"https://outlook.office365.com/IMAP.AccessAsUser.All"
};
WebServerClient consumer = new WebServerClient(server, clientID, clientSecret);
OutgoingWebResponse response = consumer.PrepareRequestUserAuthorization(
scopes, new Uri(redirectUri));
return response.AsActionResultMvc5();
}
public async Task<ActionResult> Authorized(string code, string state, string session_state)
{
List<string> scopes = new List<string>
{
"IMAP.AccessAsUser.All",
"User.Read",
"offline_access"
};
HttpClient httpClient = new HttpClient();
var values = new Dictionary<string, string>
{
{ "Host", "https://login.microsoftonline.com" },
{ "Content-Type", "application/x-www-form-urlencoded" },
{ "client_id", "[client-id here]" },
{ "scope", string.Join(" ",scopes) },
{ "code", code },
{ "redirect_uri", [redirectUri here] },
{ "grant_type", "authorization_code" },
{ "client_secret", "[client-secret here]" },
{ "state", state },
};
var content = new FormUrlEncodedContent(values);
var response = await httpClient.PostAsync("https://login.microsoftonline.com/organizations/oauth2/v2.0/token", content);
var jsonString = await response.Content.ReadAsStringAsync();
var oathToken = JsonConvert.DeserializeObject<OathToken>(jsonString);
var oauth2 = new SaslMechanismOAuth2("[Email here]", oathToken.access_token);
var stringBuilder = new StringBuilder();
using (var client = new ImapClient())
{
try
{
await client.ConnectAsync("outlook.office365.com", 993, SecureSocketOptions.Auto);
await client.AuthenticateAsync(oauth2);
var inbox = client.Inbox;
inbox.Open(FolderAccess.ReadOnly);
for (int i = 0; i < inbox.Count; i++)
{
var message = inbox.GetMessage(i);
stringBuilder.AppendLine($"Subject: {message.Subject}");
}
await client.DisconnectAsync(true);
return Content(stringBuilder.ToString());
}
catch (Exception e)
{
return Content(e.Message);
}
}
}
The problems occurs on this line: await client.AuthenticateAsync(oauth2);
I receive an error saying "Authentication failed"
However, when using the access-token from the console application in the web-application i do not get this error and can successfully authenticate and read emails in the web-application.
Can anyone point me in the right direction?
Thanks.
I'm trying to call Send on the GmailService from a C# .NET MVC app. and I keep getting a 403 error when I call send.
I've checked my scopes, the Gmail setup definitely has the Gmail API enabled, and my ClientID and ClientSecret are fresh.
var httpClient = new HttpClient{
BaseAddress = new Uri("https://www.googleapis.com")
};
var requestUrl = $"oauth2/v4/token?code={code}&client_id={ClientId}&client_secret={SecretKey}&redirect_uri={RedirectUrl}&grant_type=authorization_code";
var dict = new Dictionary<string, string>{
{ "Content-Type", "application/x-www-form-urlencoded" }
};
var req = new HttpRequestMessage(HttpMethod.Post, requestUrl){Content = new FormUrlEncodedContent(dict)};
var response = await httpClient.SendAsync(req);
var token = JsonConvert.DeserializeObject<GmailToken>(await response.Content.ReadAsStringAsync());
Session["user"] = token.AccessToken;
//var obj = await GetuserProfile(token.AccessToken);
var obj = await DoSendEmail(token);
public void DoSendEmail(GmailToken inToken) {
const string fromAcct = "XXXXXXXX#gmail.com";
TokenResponse token = new TokenResponse();
token.AccessToken = inToken.AccessToken;
token.ExpiresInSeconds = inToken.ExpiresIn;
token.IdToken = inToken.IdToken;
token.TokenType = inToken.TokenType;
token.IssuedUtc = DateTime.UtcNow;
IAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer {
ClientSecrets = secrets,
Scopes = SCOPES,
ProjectId = "Xcent CP"
});
UserCredential credential = new UserCredential(flow, fromAcct, token);
if (credential.Token.IsExpired(credential.Flow.Clock)) {
bool success = credential.RefreshTokenAsync(CancellationToken.None).Result;
if (!success) {
throw new Exception("Could not refresh token");
}
}
GmailService gs = null;
try {
gs = new GmailService(new Google.Apis.Services.BaseClientService.Initializer() {
ApplicationName = APP_NAME,
HttpClientInitializer = credential
});
var mailMessage = new System.Net.Mail.MailMessage();
mailMessage.From = new System.Net.Mail.MailAddress(fromAcct);
mailMessage.To.Add("XXXXXXXX#comcast.net");
mailMessage.ReplyToList.Add(fromAcct);
mailMessage.Subject = "Test email";
mailMessage.Body = "<html><body>Hi <b>Lee</b>, this is <b>yet another</b> test message.</body></html>";
mailMessage.IsBodyHtml = true;
var mimeMessage = MimeKit.MimeMessage.CreateFromMailMessage(mailMessage);
var gmailMessage = new Google.Apis.Gmail.v1.Data.Message {
Raw = Encode(mimeMessage.ToString())
};
gs.Users.Messages.Send(gmailMessage, fromAcct).Execute();
}
catch (Exception ex) {
throw ex;
}
finally {
if (gs != null) {
gs.Dispose();
}
gs = null;
}
}
I'm not sure where to look...I've been through many many many online articles and tutorials, tried seemingly everything, and I'm still stuck with the 403 error. Help!
Thanks,
Lee
So after many hours spent looking at this I figured out the problem. My link to the Google login was this:
Response.Redirect($"https://accounts.google.com/o/oauth2/v2/auth?client_id={ClientId}&response_type=code&scope=openid%20email%20profile&redirect_uri={RedirectUrl}&state=abcdef");
"openid%20email%20profile" was the only scope I was specifying for the login, hence the 403 error about the scope I was using for the flow variable.
phew!
I have some issues getting the access_token of the Identity Server 4 in my ASP Net 4.7.2 client application to call an API.
In ASP .Net Core client, I can get the access_token like this:
public async Task<IActionResult> CallApi()
{
var accessToken = await HttpContext.GetTokenAsync("access_token");
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var content = await client.GetStringAsync("http://localhost:5001/identity");
ViewBag.Json = JArray.Parse(content).ToString();
return View("Json");
}
Just the simple:
var accessToken = await HttpContext.GetTokenAsync("access_token");
But how can I get the access_token in my ASP Net 4.x client?
My Startup code looks like this:
public class Startup
{
public void Configuration(IAppBuilder app)
{
var authority = JwtSecurityTokenHandler.DefaultInboundClaimTypeMap = new Dictionary<string, string>();
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Cookies",
CookieName = "CustomIdentityCookie"
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
//AuthenticationType = "oidc",
Authority = "http://localhost:5000",
RedirectUri = "http://localhost:57319/signin-oidc",
PostLogoutRedirectUri = "http://localhost:57319/signout-callback-oidc",
ClientId = "mvc472",
ClientSecret = "secret",
ResponseType = "code id_token",
Scope = "api01 openid profile offline_access",
// for debug
RequireHttpsMetadata = false,
UseTokenLifetime = false,
SignInAsAuthenticationType = "Cookies"
});
}
}
I got a solution. Hope anybody can tell me if this solution is ok.
I used the workaround from the IdentityServer3 like this: IdentityServer 3 Mvc Client
I edited the depricated methods and updated to Identity Server 4.
It worked, except the logout: I get not redirected to the MVC App after logout from Identity Server. I get a 404 Error, can't find the /signout-callback-oidc route.
Here the new Startup class:
public class Startup
{
public void Configuration(IAppBuilder app)
{
//var authority = JwtSecurityTokenHandler.DefaultInboundClaimTypeMap = new Dictionary<string, string>();
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap = new Dictionary<string, string>();
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Cookies"
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
AuthenticationType = "oidc",
Authority = "http://localhost:5000",
RedirectUri = "http://localhost:57319/signin-oidc",
PostLogoutRedirectUri = "http://localhost:57319/signout-callback-oidc",
ClientId = "mvc472",
ClientSecret = "secret",
ResponseType = "code id_token",
Scope = "api01 openid profile offline_access",
// for debug
RequireHttpsMetadata = false,
UseTokenLifetime = false,
SignInAsAuthenticationType = "Cookies",
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = async n =>
{
var client = new HttpClient();
DiscoveryResponse disco = await client.GetDiscoveryDocumentAsync("http://localhost:5000");
var tokenResponse = await client.RequestAuthorizationCodeTokenAsync(new AuthorizationCodeTokenRequest
{
Address = disco.TokenEndpoint,
RedirectUri = "http://localhost:57319/signin-oidc",
ClientId = "mvc472",
ClientSecret = "secret",
Code = n.Code
});
var userInfoResponse = await client.GetUserInfoAsync(new UserInfoRequest
{
Address = disco.UserInfoEndpoint,
Token = tokenResponse.AccessToken
});
var id = new ClaimsIdentity(n.AuthenticationTicket.Identity.AuthenticationType);
id.AddClaims(userInfoResponse.Claims);
id.AddClaim(new Claim("access_token", tokenResponse.AccessToken));
id.AddClaim(new Claim("expires_at", DateTime.Now.AddSeconds(tokenResponse.ExpiresIn).ToLocalTime().ToString()));
id.AddClaim(new Claim("refresh_token", tokenResponse.RefreshToken));
id.AddClaim(new Claim("id_token", tokenResponse.IdentityToken));
id.AddClaim(new Claim("sid", n.AuthenticationTicket.Identity.FindFirst("sid").Value));
n.AuthenticationTicket = new AuthenticationTicket(
new ClaimsIdentity(id.Claims, n.AuthenticationTicket.Identity.AuthenticationType, "name", "role"),
n.AuthenticationTicket.Properties
);
},
// noch nicht getestet
RedirectToIdentityProvider = n =>
{
// if signing out, add the id_token_hint
if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.Logout)
{
var idTokenHint = n.OwinContext.Authentication.User.FindFirst("id_token");
if (idTokenHint != null)
{
n.ProtocolMessage.IdTokenHint = idTokenHint.Value;
}
}
return Task.FromResult(0);
}
}
});
}
}
i just got started in identityserver4 and couldn't figure out why the user-info endpoint is returning a forbidden status
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientId = "client1",
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
ClientSecrets =
{
new Secret("secret".Sha256())
},
AllowedScopes =
{
"api1",
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
},
IncludeJwtId = true,
RequireConsent = false,
AlwaysIncludeUserClaimsInIdToken = true,
AlwaysSendClientClaims = true,
}
};
}.
my users :
public static List<TestUser> GetUsers()
{
return new List<TestUser>
{
new TestUser
{
SubjectId = "1",
Username = "admin",
Password = "admin",
Claims = new List<Claim>
{
new Claim("Name", "test")
}
}
};
}
i'm requesting this way :
var disco = DiscoveryClient.GetAsync("https://localhost:44327").Result;
var tokenClient = new TokenClient(disco.TokenEndpoint, "mvc", "secret");
var tokenResponse = tokenClient.RequestResourceOwnerPasswordAsync("api1","admin","admin").Result;
if (tokenResponse.IsError)
{
Console.WriteLine(tokenResponse.Error);
return;
}
var client = new HttpClient();
client.SetBearerToken(tokenResponse.AccessToken);
var res = client.GetAsync(disco.UserInfoEndpoint).Result;
var claims = res.Content;
why i'm getting a forbidden status code in the userinfo endpoint ?
any help is appreciated .
First check your client configuration. In the GetClients() you have a ClientId = "client1" and then in the token client build-up you have var tokenClient = new TokenClient(disco.TokenEndpoint, "mvc", "secret");.
Your clientId is not correct. Where is this mvc coming from?
the forbidden response can also be the result of the incorrect scope , in case you want to get some information about the user from the userinfo endpoint you must include OpenId scope in the used token , when you decode your token you must have the openid scope in it like in the picture openid scope