Reputation: 43748
I have a WCF service that is using a custom ServiceAuthorizationManager
. The custom auth manager is already set up to handle Windows and Forms authentication.
However, if I connect with a client that is set to UserName
auth, I can't seem to find the username anywhere.
The client code looks like this:
this.ClientCredentials.UserName.UserName = "user";
this.ClientCredentials.UserName.Password = "pass";
this.Open();
this.MyMethod(); // my actual contract method
this.Close();
Then on the server, I have my custom auth manager:
public sealed class AppAuthorizationManager : ServiceAuthorizationManager
{
public override bool CheckAccess(OperationContext operationContext, ref Message message)
{
// would like to check user/pwd here...
}
}
Is this possible?
Thread.CurrentPrincipal
is not set, operationContext.ServiceSecurityContext.PrimaryIdentity
is not set.operationContext.ServiceSecurityContext.AuthorizationContext.ClaimSets
is empty. Is the user/pwd supposed to be available anywhere? Or do I have to add a custom UsernamePasswordValidator
too?
Update: So I added a custom UserNamePasswordValidator
and an IAuthorizationPolicy
.
My updated WCF config looks like this:
<behavior name="Server2ServerBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceAuthorization principalPermissionMode="Custom" serviceAuthorizationManagerType="MyApp.AuthManager, MyApp">
<authorizationPolicies>
<add policyType="MyApp.TokenAuthorizationPolicy, MyApp" />
</authorizationPolicies>
</serviceAuthorization>
<serviceCredentials>
<userNameAuthentication customUserNamePasswordValidatorType="MyApp.PFUserNameValidator, MyApp" />
</serviceCredentials>
</behavior>
If I set a breakpoint in all 3 of those classes, WCF throws the exception:
LogonUser failed for the 'username' user. Ensure that the user has a valid Windows account.
at System.IdentityModel.Selectors.WindowsUserNameSecurityTokenAuthenticator.ValidateUserNamePasswordCore(String userName, String password)
Before any of them are run. Hmmm...
Upvotes: 4
Views: 11326
Reputation: 1063964
This is normally handled in the UsernamePasswordValidator - which is the only place you'll have access to the password. However, this isn't where you set the principal - that would be in the IAuthorizationPolicy
's Evaluate
method, which might look something like:
bool IAuthorizationPolicy.Evaluate(
EvaluationContext evaluationContext, ref object state)
{
IList<IIdentity> idents;
object identsObject;
if (evaluationContext.Properties.TryGetValue(
"Identities", out identsObject) && (idents =
identsObject as IList<IIdentity>) != null)
{
foreach (IIdentity ident in idents)
{
if (ident.IsAuthenticated &&
ident.AuthenticationType == TrustedAuthType)
{
evaluationContext.Properties["Principal"]
= //TODO our principal
return true;
}
}
}
if (!evaluationContext.Properties.ContainsKey("Principal"))
{
evaluationContext.Properties["Principal"] = //TODO anon
}
return false;
}
(where TrustedAuthType
is the name of our password validator)
With this in place, the thread's principal will be set, and we can identify ourselves (and use roles-based security etc)
Upvotes: 6