IdentityServer3 symmetric key issue on Relying Party - c#

I just set up a SelfHost(InMem with WS-Fed) Thinktecture IdentityServer3 project example and I'm trying to use it to get a JWT, the problem is that I only recieve tokens signed with an asymmetric key using the alg RS256 but I need them to be symmetric using the alg HS256 so I can use the same key on the client.
I have tried to follow some examples by configuring the Relying Party on the server with no success.
For example, I see the following markup:
var relyingParty = new RelyingParty()
{
Enabled = true,
Realm = "urn:carbon",
Name = "Test party",
SymmetricSigningKey =
Convert.FromBase64String("R03W9kJERSSLH11Px+R/O7EYfAadSMQfZD5haQZj6eU="),
TokenLifeTime = 120
};
But when I try it on my code, I have an error on SymmetricSigningKey and it says that:
'Thinktecture.IdentityServer.WsFederation.Models.RelyingParty' does
not contain a definition for 'SymmetricSigningKey'
What am I doing wrong?, thanks in advance!
UPDATE
Markup of the startup file:
public void Configuration(IAppBuilder appBuilder)
{
var factory = InMemoryFactory.Create(
users: Users.Get(),
clients: Clients.Get(),
scopes: Scopes.Get()
);
var options = new IdentityServerOptions
{
IssuerUri = "https://idsrv3.com",
SiteName = "Thinktecture IdentityServer3 - WsFed",
SigningCertificate = Certificate.Get(),
Factory = factory,
PluginConfiguration = ConfigurePlugins,
};
appBuilder.UseIdentityServer(options);
}
private void ConfigurePlugins(IAppBuilder pluginApp, IdentityServerOptions options)
{
var wsFedOptions = new WsFederationPluginOptions(options);
// data sources for in-memory services
wsFedOptions.Factory.Register(new Registration<IEnumerable<RelyingParty>>(RelyingParties.Get()));
wsFedOptions.Factory.RelyingPartyService = new Registration<IRelyingPartyService>(typeof(InMemoryRelyingPartyService));
pluginApp.UseWsFederationPlugin(wsFedOptions);
}
Markup of the scope used:
new Scope
{
Name = "api1"
}
Markup of the client used:
new Client
{
ClientName = "Silicon on behalf of Carbon Client",
ClientId = "carbon",
Enabled = true,
AccessTokenType = AccessTokenType.Jwt,
Flow = Flows.ResourceOwner,
ClientSecrets = new List<ClientSecret>
{
new ClientSecret("21B5F798-BE55-42BC-8AA8-0025B903DC3B".Sha256())
}
}
Markup of the user used:
new InMemoryUser{Subject = "bob", Username = "bob", Password = "bob",
Claims = new Claim[]
{
new Claim(Constants.ClaimTypes.GivenName, "Bob"),
new Claim(Constants.ClaimTypes.FamilyName, "Smith"),
new Claim(Constants.ClaimTypes.Email, "BobSmith#email.com")
}
}
UPDATE
I just check the class model of the relying party of IdentityServer3 and there's no property for the symmetric signing key... I'm lost...
Any ideas?

Related

ASP.NET Core Identity Server - Hybrid Grant Flow Implementation?

I have a .NET Core 6.0 project that I implemented IdentityServer4.
When I use ResourceOwnerPassword or ClientCredentials grant flow, there is no problem...
But when I use Hybrid grant flow, I don't know how to do that...
This is how I implement ResourceOwnerPassword grant type for my project..
IdentityServer project Client (Resource Owner Password Grant Type)
new Client
{
ClientName="WebUI First For User",
ClientId="WebUI_First_User",
AllowOfflineAccess=true,
ClientSecrets= {new Secret("first_user".Sha256())},
AllowedGrantTypes= GrantTypes.ResourceOwnerPassword,
AllowedScopes={ "catalog_fullpermission",
"webui_user_first",
"gateway_fullpermission",
"roles",
IdentityServerConstants.StandardScopes.Email,
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.OfflineAccess,
IdentityServerConstants.LocalApi.ScopeName
},
AccessTokenLifetime=1*60*60,
RefreshTokenExpiration=TokenExpiration.Absolute,
AbsoluteRefreshTokenLifetime= (int) (DateTime.Now.AddDays(60)- DateTime.Now).TotalSeconds,
RefreshTokenUsage= TokenUsage.ReUse
}
IdentityServer project Client (Client Credentials Grant Type)
new Client
{
ClientName="WebUI First",
ClientId="WebUI_First",
ClientSecrets= {new Secret("first".Sha256())},
AllowedGrantTypes= GrantTypes.ClientCredentials,
AllowedScopes={ "catalog_fullpermission",
"webui_first",
"gateway_fullpermission",
IdentityServerConstants.LocalApi.ScopeName,
IdentityServerConstants.StandardScopes.OpenId
}
}
SignIn Method
var disco = await _httpClient.GetDiscoveryDocumentAsync(new DiscoveryDocumentRequest
{
Address = _serviceApiSettings.IdentityBaseUri,
Policy = new DiscoveryPolicy { RequireHttps = false }
});
if (disco.IsError)
{
throw disco.Exception;
}
var passwordTokenRequest = new PasswordTokenRequest
{
ClientId = _clientSettings.WebClientForUser.ClientId,
ClientSecret = _clientSettings.WebClientForUser.ClientSecret,
UserName = signinInput.Email,
Password = signinInput.Password,
Address = disco.TokenEndpoint
};
//This is how I get token with ResourceOwnerPassword
var token = await _httpClient.RequestPasswordTokenAsync(passwordTokenRequest);
...
...
IdentityServer project Client (Hybrid Grant Type)
new Client
{
ClientName = "MVC Client",
ClientId = "mvc-client",
AllowedGrantTypes = GrantTypes.Hybrid,
RedirectUris = new List<string>{ "https://localhost:5001/signin-oidc" },
RequirePkce = false,
AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile },
ClientSecrets = { new Secret("MVCSecret".Sha512()) }
}
Here I use PasswordTokenRequest class and RequestPasswordTokenAsync method which comes as an extension method...
But I don't know which method I should use for Hybrid grant flow and how to...
Is there anyone having the same issue and solved the problem?
..

Google People Api Contact is created but I can't see it anywhere

I'm using PeopleService in order to create contacts straight into my G Suit Account. I followed the security steps about getting the key for a type of Service Account. My Application will create contacts so that there's no need to request a specific user permission. It has its own key and credentials.
My code seems to work except because the CreateContactRequest provides me ResourceName with value "people/c171255166120767303". And every time I request I got a different resourceName like this "people/c9013378989213012841".
The problem is, where the hell that goes? At my G Suite account, I can’t see the created contact anywhere.
But the resulting Person object seems to be ok.
How can I check if this works? Where the contact was created?
The code is as bellow:
private static string _clientId = "1........1";
private static string _clienteScret = "i******************_";
private static string _serviceAccountId = "aaaa#bbbb.iam.gserviceaccount.com";
public static void Cadastrar(Models.SignupRequest message)
{
var chave =
#"D:\********.p12";
var certificate = new X509Certificate2(chave, "notasecret", X509KeyStorageFlags.Exportable);
var credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(_serviceAccountId)
{
Scopes = new[] {
PeopleService.Scope.Contacts,
PeopleService.Scope.ContactsReadonly,
"https://www.google.com/m8/feeds"
}
}.FromCertificate(certificate));
var service = new PeopleService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Client for www",
});
var contactToCreate = new Person();
var names = new List<Name> {new Name() {GivenName = message.name, FamilyName = "Doe"}};
contactToCreate.Names = names;
contactToCreate.EmailAddresses = new List<EmailAddress>
{
new EmailAddress()
{
DisplayName = message.name,
Value = message.email
}
};
contactToCreate.Organizations = new List<Organization>
{
new Organization()
{
Current = true,
Name = message.nome_fantasia,
}
};
contactToCreate.Biographies = new List<Biography>
{
new Biography()
{
Value = message.ToString()
}
};
contactToCreate.PhoneNumbers = new List<PhoneNumber>
{
new PhoneNumber()
{
Type = "mobile",
Value = message.celular
},
new PhoneNumber()
{
Type = "work",
Value = message.telefone
}
};
var request = new Google.Apis.PeopleService.v1.PeopleResource.CreateContactRequest(service, contactToCreate);
var createdContact = request.Execute();
}
If you want to create contact for user with 'email#yourdomain.com' through a service account you need:
Use "setServiceAccountUser(userEmail)" to impersonate the user
Enable impersonation on your G-Suite for People API service. For this point:
See section "Delegating domain wide access" here
Get client-id from your credential account
Use "https://www.googleapis.com/auth/contacts" as scope.

Getting claims in identity server using resource owner password

I am using identity server 4 for authentication using grant type as 'ResourceOwnerPassword'. I am able to authenticate the user but not able to get claims related to user. So how can I get those ?
Below is my code
Client
Startup.cs
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = "http://localhost:5000",
RequireHttpsMetadata = false,
ApiName = "api1"
});
Controller
public async Task<IActionResult> Authentication(LoginViewModel model)
{
var disco = await DiscoveryClient.GetAsync("http://localhost:5000");
// request token
var tokenClient = new TokenClient(disco.TokenEndpoint, "ro.client", "secret");
var tokenResponse = await tokenClient.RequestResourceOwnerPasswordAsync(model.Email, model.Password, "api1");
if (tokenResponse.IsError)
{
Console.WriteLine(tokenResponse.Error);
}
// Here I am not getting the claims, it is coming Forbidden
var extraClaims = new UserInfoClient(disco.UserInfoEndpoint);
var identityClaims = await extraClaims.GetAsync(tokenResponse.AccessToken);
if (!tokenResponse.IsError)
{
Console.WriteLine(identityClaims.Json);
}
Console.WriteLine(tokenResponse.Json);
Console.WriteLine("\n\n");
}
Server
Startup.cs
services.AddIdentityServer()
.AddTemporarySigningCredential()
.AddInMemoryPersistedGrants()
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients(Configuration))
.AddAspNetIdentity<ApplicationUser>()
.AddProfileService<IdentityProfileService>()
.AddResourceOwnerValidator<ResourceOwnerPasswordValidator>();
Config.cs
public static IEnumerable<Client> GetClients(IConfigurationRoot Configuration)
{
// client credentials client
return new List<Client>
{
// resource owner password grant client
new Client
{
ClientId = "ro.client",
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
ClientSecrets =
{
new Secret("secret".Sha256())
},
AlwaysSendClientClaims = true,
AlwaysIncludeUserClaimsInIdToken = true,
AccessTokenType = AccessTokenType.Jwt
}
};
}
public static IEnumerable<ApiResource> GetApiResources()
{
return new List<ApiResource>
{
new ApiResource("api1", "My API")
};
}
But when I check my access token in jwt.io there I can see the claims But why I am not able to get in the controller ?
Any help on this appreciated !
You can call the UserInfoEndpoint, as per your example, but you can also get additional claims if you define your ApiResource as requiring them.
For example, rather than just defining your ApiResource like you are:
new ApiResource("api1", "My API")
You can use the expanded format and define what UserClaims you'd like to have when getting an access token for this scope.
For example:
new ApiResource
{
Name = "api1",
ApiSecrets = { new Secret(*some secret*) },
UserClaims = {
JwtClaimTypes.Email,
JwtClaimTypes.PhoneNumber,
JwtClaimTypes.GivenName,
JwtClaimTypes.FamilyName,
JwtClaimTypes.PreferredUserName
},
Description = "My API",
DisplayName = "MyApi1",
Enabled = true,
Scopes = { new Scope("api1") }
}
Then in your own implementation of the IProfileService you will find that calls to GetProfileDataAsync have a list of what claims are requested in the context (ProfileDataRequestContext.RequestedClaimTypes). Given that list of what's been asked for, you can then add any claims you like - however you like - to the context.IssuedClaims that you return from that method. These will then be a part of the access token.
If you only want certain claims by specifically calling the UserInfo endpoint though, you'll want to create an IdentityResource definition and have that scope included as part of your original token request.
For example:
new IdentityResource
{
Name = "MyIdentityScope",
UserClaims = {
JwtClaimTypes.EmailVerified,
JwtClaimTypes.PhoneNumberVerified
}
}
But your first problem is following the other answer here so you don't get 'forbidden' as the response to the UserInfo endpoint!
Try sending the token along the request, when calling the UserInfoEndpoint. Try this:
var userInfoClient = new UserInfoClient(doc.UserInfoEndpoint, token);
var response = await userInfoClient.GetAsync();
var claims = response.Claims;
official docs

Using Kentor + Windows Authentication on IdentityServer3

I have an IdentityServer3 working with Windows Authentication Service. Now I want to handle the SAML2 protocol on my IdentityServer3 and I saw Kentor could do it for me.
The problem is that Kentor is using OpenID Connect in all samples, I searched a while but i couldn't find any documentation on how to combine Kentor with WindowsAuth. After many tries without success, I come here to ask if it is realy possible and how ?
Here is my (non-working) configuration in Startup.cs :
public void Configuration(IAppBuilder appBuilder)
{
appBuilder.Map("/windows", ConfigureWindowsTokenProvider);
appBuilder.UseIdentityServer(GetIdentityServerOptions());
}
private void ConfigureWsFederation(IAppBuilder pluginApp, IdentityServerOptions options)
{
var factory = new WsFederationServiceFactory(options.Factory);
factory.Register(new Registration<IEnumerable<RelyingParty>>(RelyingParties.Get()));
factory.RelyingPartyService = new Registration<IRelyingPartyService>(typeof(InMemoryRelyingPartyService));
factory.CustomClaimsService = new Registration<ICustomWsFederationClaimsService>(typeof(ClaimsService));
factory.CustomRequestValidator = new Registration<ICustomWsFederationRequestValidator>(typeof(RequestValidator));
var wsFedOptions = new WsFederationPluginOptions
{
IdentityServerOptions = options,
Factory = factory,
};
pluginApp.UseWsFederationPlugin(wsFedOptions);
}
private IdentityServerOptions GetIdentityServerOptions()
{
DefaultViewServiceOptions viewServiceOptions = new DefaultViewServiceOptions();
viewServiceOptions.CustomViewDirectory = HttpContext.Current.Server.MapPath("~/Templates");
viewServiceOptions.Stylesheets.Add("/Content/Custom.css");
IdentityServerServiceFactory factory = new IdentityServerServiceFactory()
.UseInMemoryClients(new List<Client>())
.UseInMemoryScopes(new List<Scope>());
factory.ConfigureDefaultViewService(viewServiceOptions);
factory.UserService = new Registration<IUserService>(resolver => new UserService());
return new IdentityServerOptions
{
SigningCertificate = Certificate.Load(),
Factory = factory,
PluginConfiguration = ConfigureWsFederation,
EventsOptions = new EventsOptions
{
RaiseSuccessEvents = true,
RaiseFailureEvents = true,
},
AuthenticationOptions = new IdentityServer3.Core.Configuration.AuthenticationOptions
{
IdentityProviders = ConfigureIdentityProviders,
EnableLocalLogin = false,
},
RequireSsl = true,
};
}
private void ConfigureIdentityProviders(IAppBuilder app, string signInAsType)
{
ConfigureWSFederationProvider(app, signInAsType);
ConfigureKentorProvider(app, signInAsType);
}
private void ConfigureKentorProvider(IAppBuilder app, string signInAsType)
{
SPOptions spOptions = new SPOptions
{
EntityId = new EntityId("Dropbox"),
};
KentorAuthServicesAuthenticationOptions kentorOptions = new KentorAuthServicesAuthenticationOptions(false)
{
Caption = "Windows",
SignInAsAuthenticationType = signInAsType,
SPOptions = spOptions,
};
IdentityProvider idp = new IdentityProvider(new EntityId("http://stubidp.kentor.se/Metadata"), spOptions)
{
Binding = Saml2BindingType.HttpRedirect,
AllowUnsolicitedAuthnResponse = true,
LoadMetadata = true,
};
kentorOptions.IdentityProviders.Add(idp);
app.UseKentorAuthServicesAuthentication(kentorOptions);
}
private void ConfigureWSFederationProvider(IAppBuilder app, string signInAsType)
{
app.UseWsFederationAuthentication(new WsFederationAuthenticationOptions()
{
AuthenticationType = "windows",
Caption = "Windows",
SignInAsAuthenticationType = signInAsType,
MetadataAddress = serverHost + "windows",
Wtrealm = "urn:idsrv3",
});
}
private void ConfigureWindowsTokenProvider(IAppBuilder app)
{
app.UseWindowsAuthenticationService(new WindowsAuthenticationOptions
{
IdpReplyUrl = serverHost,
SigningCertificate = Certificate.Load(),
EnableOAuth2Endpoint = false,
});
}
This configuration builds, but when I use the Dropbox SSO (using SAML2) I get the exception No Idp with entity id "Dropbox" found.
You've configured "Dropbox" as the identity (EntityId in SAML2 terms) of your application (the one in SpOptions). That should be a URI that identifies your application. The convention is to use the URL to the metadata (~/AuthServices).
You need to add an IdentityProvider with the settings of the dropbox idp. Please also note that an EntityId of "Dropbox" won't work as the SAML2 standard requires an Entity ID to be an absolute URI.

ServiceStack GetSession during heartbeat

I have a servicestack app in which I would like to make some session-related updates during heartbeat.
My simplified host code:
internal class SelfHost : AppSelfHostBase
{
public SelfHost() : base("HttpListener Self-Host", typeof(ServerEventsService).Assembly)
{
}
public override void Configure(Container container)
{
Plugins.Add(new ServerEventsFeature
{
LimitToAuthenticatedUsers = true,
NotifyChannelOfSubscriptions = false,
OnHeartbeatInit = req =>
{
var userSession = req.GetSession();
var sessionKey = SessionFeature.GetSessionKey(req.GetSessionId());
}
});
container.Register<ICacheClient>(new MemoryCacheClient());
Plugins.Add(new AuthFeature(() => new AuthUserSession(),
new IAuthProvider[]
{
new CredentialsAuthProvider
{
SessionExpiry = TimeSpan.FromSeconds(30),
SkipPasswordVerificationForInProcessRequests = true,
},
})
);
var userRep = new InMemoryAuthRepository();
container.Register<IUserAuthRepository>(userRep);
string hash;
string salt;
var pwd = "ValidPassword";
new SaltedHash().GetHashAndSaltString(pwd, out hash, out salt);
userRep.CreateUserAuth(new UserAuth
{
Id = 3,
DisplayName = "CustomDisplayName2",
Email = "test2#gmail.com",
UserName = "CustomUserName2",
FirstName = "FirstName2",
LastName = "LastName2",
PasswordHash = hash,
Salt = salt,
}, pwd);
}
}
and the simplified client code:
var mainClient = new ServerEventsClient(baseUrl, "home");
var authResponse = mainClient.Authenticate(new Authenticate
{
provider = "credentials",
UserName = "CustomUserName2",
Password = "ValidPassword",
RememberMe = false,
});
mainClient.Connect().Wait();
It connects and authenticates fine, the problematic part start in OnHeartbeatInit - my session here is always a new non authenticated session that has only Id, CreatedAt and LastModified. My service is marked with [Authenticate] attribute on class level. Have I misconfigured or lost some config that should allow that? If I post to my service and call Request.GetSession() then I get a populated session which I expect to see in OnHeartbeatInit as well.
Sidenote: there are no hidden configs anywhere, I have created a separate app exclusively for this example.
The issue is that the heartbeats requests sent in .NET ServerEventsClient isn't sent with the same CookieContainer that the client authenticates or connects to the /event-stream with, which is what contains the Session Cookies.
I've updated the ServerEventsClient so heartbeat requests now reference the same CookieContainer for every heartbeat request in this commit.
This change is available from v4.0.55 that's now available on MyGet.

Categories