I am configuring a asp.net mvc app (or relying party) to use thinktecture identity server. Identity Server is up and running locally and I am able to retrieve metadata from its endpoint.
Here is the code used to register the middleware:
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions("localIdp")
{
AuthenticationType = "localIdp",
ClientId = "Welfare4Partners",
MetadataAddress = "https://localhost:44333/core/.well-known/openid-configuration",
//Configuration = new OpenIdConnectConfiguration
//{
// AuthorizationEndpoint = "https://localhost:44333/core/connect/authorize",
// JwksUri = "https://localhost:44333/core/.well-known/jwks",
// TokenEndpoint = "https://localhost:44333/core/connect/token",
// UserInfoEndpoint = "https://localhost:44333/core/connect/userinfo",
// Issuer = "https://localhost:44333/core",
// EndSessionEndpoint = "https://localhost:44333/core/connect/endsession",
//},
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = (context) =>
{
return Task.FromResult(context);
},
SecurityTokenReceived = (context) =>
{
return Task.FromResult(context);
},
SecurityTokenValidated = (context) =>
{
return Task.FromResult(context);
},
AuthenticationFailed = (context) =>
{
context.HandleResponse();
context.OwinContext.Authentication.SignOut(CookieAuthenticationDefaults.AuthenticationType, context.Options.AuthenticationType);
context.SkipToNextMiddleware();
return Task.FromResult(context);
},
MessageReceived = (context) =>
{
return Task.FromResult(context);
},
RedirectToIdentityProvider = (context) =>
{
return Task.FromResult(context);
}
},
Authority = "https://localhost:44333",
RedirectUri = AppSettings.PostLoginRedirectUri,
ResponseType = OpenIdConnectResponseTypes.IdToken,
Scope = "openid",
SignInAsAuthenticationType = CookieAuthenticationDefaults.AuthenticationType
});
As you can see I have commented the Configuration property once I have the MetadataAddress prop set.
I am calling the middleware with the following line of code in an Action:
var authProperties = new AuthenticationProperties { RedirectUri = AppSettings.PostLoginRedirectUri, IsPersistent = false, };
OwinContext.Authentication.Challenge(authProperties, authenticationType);
I have verified the value of the authenticationType and it contains "localIdp". After calling the challenge nothing happens. Odd think is that if I comment the metadataAddress and uncomment the Configuration property, the middleware is called.
Is there a way to debug the OWIN requests in order to check what's wrong in the code?
Metadata is the following:
{
"issuer": "https://localhost:44333/core",
"jwks_uri": "https://localhost:44333/core/.well-known/jwks",
"authorization_endpoint": "https://localhost:44333/core/connect/authorize",
"token_endpoint": "https://localhost:44333/core/connect/token",
"userinfo_endpoint": "https://localhost:44333/core/connect/userinfo",
"end_session_endpoint": "https://localhost:44333/core/connect/endsession",
"check_session_iframe": "https://localhost:44333/core/connect/checksession",
"revocation_endpoint": "https://localhost:44333/core/connect/revocation",
"introspection_endpoint": "https://localhost:44333/core/connect/introspect",
"frontchannel_logout_supported": true,
"frontchannel_logout_session_supported": true,
"scopes_supported": ["openid", "profile", "email", "address", "roles", "all_claims", "offline_access", "read", "write"],
"claims_supported": ["sub", "name", "family_name", "given_name", "middle_name", "nickname", "preferred_username", "profile", "picture", "website", "gender", "birthdate", "zoneinfo", "locale", "updated_at", "email", "email_verified", "address", "role"],
"response_types_supported": ["code", "token", "id_token", "id_token token", "code id_token", "code token", "code id_token token"],
"response_modes_supported": ["form_post", "query", "fragment"],
"grant_types_supported": ["authorization_code", "client_credentials", "password", "refresh_token", "implicit", "custom2", "custom"],
"subject_types_supported": ["public"],
"id_token_signing_alg_values_supported": ["RS256"],
"code_challenge_methods_supported": ["plain", "S256"],
"token_endpoint_auth_methods_supported": ["client_secret_post", "client_secret_basic"]
}
Related
Playing around with a demo project from PluralSight, I am trying to have the IDP redirect back to the server app on sign out.
The PostLogOutRedirectUris is defined in the config for the Client at the IDP level, but it doesn't seem to have any effect.
public static IEnumerable<Client> Clients =>
new Client[]
{
new Client
{
ClientId = "bethanyspieshophr",
ClientName = "Bethany's Pie Shop HRM",
AllowOfflineAccess = true,
AccessTokenLifetime = 120,
RequireConsent = false,
RequirePkce = true,
AllowedGrantTypes = GrantTypes.Code,
ClientSecrets = {
new Secret("108B7B4F-BEFC-4DD2-82E1-7F025F0F75D0".Sha256()) },
RedirectUris = { "https://localhost:44301/signin-oidc" },
PostLogoutRedirectUris = { "https://localhost:44301/signout-oidc" },
AllowedScopes = { "openid", "profile", "email", "bethanyspieshophrapi" }
}
};
If I manually at runtime set the LoggedOutViewModel it works as expected.
How are you performing the logout request? Remember that the value in the client settings is just the registered value which is verified during logout request. The user is not redirected automatically to the postLogoutRedirectUri. You have to pass a post_logout_redirect_uri parameter to the end session endpoint and this parameter must match on of the values in the PostLogoutRedirectUris setting. To use this feature you should also post a valid ID token in the id_token_hint parameter, so that the server knows which client is requesting the logout.
You can have a look at the end session enpoint docs for details.
The issue was simply due to a typo, which sent me on a wild goose chase.
PostLogoutRedirectUris = { "https://localhost:44301/signout-oidc" },
Should be
PostLogoutRedirectUris = { "https://localhost:44301/signout-callback-oidc" },
And then, it worked.
I am trying to get external login with identityserver4 to work. It almost works, however, it does not auto-create or links when an external user is logged in. If I create a user in Umbraco am able to link the user manually.
I have the following configured - but I must be missing something
.AddBackOfficeExternalLogins(builder =>
builder.AddBackOfficeLogin(
build =>
build.AddOpenIdConnect(
build.SchemeForBackOffice(OpenIdConnectDefaults.AuthenticationScheme),
"External login",
options =>
{
// use cookies
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
// pass configured options along
// Use the authorization code flow
options.AuthenticationMethod = OpenIdConnectRedirectBehavior.RedirectGet;
options.RequireHttpsMetadata = false;
options.Authority = "https://localhost:3930";
options.MetadataAddress = "https://localhost:3930/.well-known/openid-configuration";
options.ClientSecret = "**";
options.ClientId = "***";
options.GetClaimsFromUserInfoEndpoint = true;
options.ResponseType = "code id_token";
options.CallbackPath = new PathString("/");
options.RemoteSignOutPath = "/oidc-signout";
options.SignedOutRedirectUri = "**";
options.Scope.Clear();
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("email");
options.SaveTokens = true;
options.UsePkce = true;
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = "role",
};
}
),
options =>
new BackOfficeExternalLoginProviderOptions(
"btn-primary", // button stype
"fa-windows", // icon
new ExternalSignInAutoLinkOptions(autoLinkExternalAccount: true, defaultUserGroups: new[] { Constants.Security.EditorGroupAlias }, defaultCulture: "en-US", allowManualLinking: true)
{
OnAutoLinking = (user, loginInfo) =>
{
//
},
OnExternalLogin = (user, loginInfo) =>
{
return true;
}
},
true, // deny local login
false, // autoredirect local login to external login
null) // custom backoffice view
)
)
I am getting Invalid authorization code{"code": "MyTestCode"},
Here is more detailed error:
Invalid authorization code{"code": "MyTestCode"}, details: {"ClientId": "AuthorizationCodeClientFlow", "ClientName": "Authorization Code Client", "GrantType": "authorization_code", "Scopes": null, "AuthorizationCode": "MyTestCode", "RefreshToken": null, "UserName": null, "AuthenticationContextReferenceClasses": null, "Tenant": null, "IdP": null, "Raw": {"grant_type": "authorization_code", "code": "MyTestCode", "redirect_uri": "https://localhost:5000/oauth/callback", "client_id": "AuthorizationCodeClientFlow"}, "$type": "TokenRequestValidationLog"} <s:IdentityServer4.Validation.TokenRequestValidator>
I am testing using Postman
This is client generated from this code :
{
ClientName = "Authorization Code Client",
ClientId = "AuthorizationCodeClientFlow",
AllowedGrantTypes = GrantTypes.Code,
ClientSecrets =
{
new Secret("AuthorizationCodeClientFlowSecret".Sha512())
},
AllowedScopes =
{
"all"
},
RedirectUris =
new List<string> {
"https://localhost:5000/oauth/callback"
},
AllowOfflineAccess = false,
AccessTokenLifetime = 60
};
https://localhost:5105/oauth/authorize works fine. I get error in https://localhost:5105/oauth/token step. When I validate request like that:
var form = (await _httpContextAccessor.HttpContext.Request.ReadFormAsync()).AsNameValueCollection();
var validationResult = await _requestValidator.ValidateRequestAsync(form, clientResult);
if (validationResult.IsError)
{
return new IdpTokenResponse
{
Custom = new Dictionary<string, object>
{
{ "Error", validationResult.Error },
{ "ErrorDescription", validationResult.ErrorDescription }
}
};
}
You need to take the authorization code that you receive from the initial authentication request and then take it and pass it along when you get the token from the token endpoint.
one unrelated thing is that you should always ask for the openid scope when you authenticate against IdentityServer, All or "" is not valid.
So we've set up an IDP (IdentityServer4, Core2) and have been using it for our own applications without problems (Implicit Flow). Now though, one of our partners will be using our IDP to make API requests from another application.
We've setup the ApiResources:
new ApiResource("api", "API",
new List<string>() {
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Email,
IdentityServerConstants.StandardScopes.Profile,
"role",
"team"
})
The client in question:
new Client {
ClientName= "ClientName",
Description = "ClientDescription",
LogoUri = "/img/ClientLogos/clientLogo.png",
ClientUri = "https://client.url",
RedirectUris = {
"https://...",
"https://...",
"http://...",
"http://..."
},
AllowedGrantTypes = GrantTypes.Code,
RequireConsent = true,
ClientId = "clientId",
AllowedScopes = { "api" },
ClientSecrets = { new Secret("clientSecret".Sha256()) },
AlwaysSendClientClaims = true,
AllowOfflineAccess = true,
Claims = {
...
}
}
I (wrongfully) assumed that since the client has the "api" scope, which in turn has the "OpenID" and "Profile" scope, the client would automatically gain authorization to use the UserInfo endpoint, but they are getting the "Forbidden" StatusCode.
Can someone explain to me what we're doing wrong here?
I think you need to include IdentityResources as well, because it dictates whats part of the ID-token and what is available from the UserInfo endpoint.
I'm tying to figure out OAuth2.0, OIDC1.0 and IdentityServer4. I've setup a test MVC Core client with only "openid" scope requested. But somehow OpenIdConnnect middleware keeps adding "profile" scope to the requested scopes. Is "profile" a mandatory scope? Should I enable it? Or what am I doing wrong here? I'd appreciate any input.
IdSrv resources:
_identityResources = new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResource
{
Name = "test_user",
UserClaims = new[] { "test_user.email" }
}
};
_apiResources = new List<ApiResource>
{
new ApiResource
{
Name = "test_api",
Scopes =
{
new Scope()
{
Name = "test_api.account.create",
UserClaims = new[] { "test_api.account.create" }
}
}
}
};
IdSrv client config:
new Client
{
ClientId = "client.mvcx",
ClientName = "MVC Core Client",
AllowedGrantTypes = GrantTypes.Hybrid,
AllowAccessTokensViaBrowser = false,
ClientSecrets =
{
new Secret("secret".Sha256())
},
RedirectUris = { Common.Addresses.Client + "/signin-oidc" },
PostLogoutRedirectUris = { Common.Addresses.Client },
LogoutUri = Common.Addresses.Client + "/signout-oidc",
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId
},
AllowOfflineAccess = false,
RequireConsent = false,
AlwaysIncludeUserClaimsInIdToken = true
},
MVC Client:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationScheme = "cookies",
AutomaticAuthenticate = true,
ExpireTimeSpan = TimeSpan.FromMinutes(60)
});
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
AuthenticationScheme = "oidc",
SignInScheme = "cookies",
Authority = Common.Addresses.IdSrv,
RequireHttpsMetadata = false,
ClientId = "client.mvcx",
ClientSecret = "secret",
ResponseType = "code id_token",
Scope = { "openid" },
SaveTokens = true,
TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
NameClaimType = IdentityModel.JwtClaimTypes.Name,
RoleClaimType = IdentityModel.JwtClaimTypes.Role,
},
IdSrv error:
info: IdentityServer4.Hosting.IdentityServerMiddleware[0]
Invoking IdentityServer endpoint: IdentityServer4.Endpoints.AuthorizeEndpoint for /connect/authorize
fail: IdentityServer4.Validation.ScopeValidator[0]
Invalid scope: profile
fail: IdentityServer4.Endpoints.AuthorizeEndpoint[0]
Request validation failed
info: IdentityServer4.Endpoints.AuthorizeEndpoint[0]
{
"ClientId": "client.mvcx",
"ClientName": "MVC Core Client",
"RedirectUri": "http://localhost:32579/signin-oidc",
"AllowedRedirectUris": [
"http://localhost:32579/signin-oidc"
],
"SubjectId": "anonymous",
"ResponseType": "code id_token",
"ResponseMode": "form_post",
"GrantType": "hybrid",
"RequestedScopes": "openid profile",
...
The OpenIdConnectionOptions automatically requests the openid and profile scopes (see source code), with a private setter on the Scope property.
When you set scopes like you are, you are not setting a new list, but adding to the existing.
Clearing and then adding the scope works:
var options = new OpenIdConnectOptions();
options.Scope.Clear();
options.Scope.Add("openid");
app.UseOpenIdConnectAuthentication(options);