Custom Authentication
I'm having an issue trying to implement a custom MembershipDataProvider.
I followed this tutorial and everything seems to work OK. My code runs without error, but after a succesful login I get the following error:
Exception information:
Exception type: ArgumentNullException
Exception message: Value cannot be null.
Parameter name: value
at Microsoft.IdentityModel.Claims.Claim..ctor(String claimType, String value, String valueType, String issuer, String originalIssuer)
at Microsoft.IdentityModel.Claims.Claim..ctor(String claimType, String value, String valueType, String issuer)
at Microsoft.IdentityModel.Claims.Claim..ctor(String claimType, String value)
at Telerik.Sitefinity.Security.SitefinityIdentity..ctor(User user, Boolean newLoginDate)
at Telerik.Sitefinity.Security.Claims.SecurityTokenServiceHttpHandler.SendSimpleWebToken(HttpContextBase context, User user, RequestMessage reqMessage)
at Telerik.Sitefinity.Security.Claims.SecurityTokenServiceHttpHandler.SendToken(HttpContextBase context, User user, RequestMessage reqMessage, String format)
at Telerik.Sitefinity.Security.Claims.SecurityTokenServiceHttpHandler.ProcessRequest(HttpContextBase context)
at Telerik.Sitefinity.Security.Claims.SecurityTokenServiceHttpHandler.ProcessRequest(HttpContext context)
at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean & completedSynchronously)
When I use the default provider everything work ok.
I'm manually creating the user object and I don't know if thats the proper way of doing it.
Any idea?
Hello Lucas,
I have test the code from the tutorial using the full code sample of the custom provider and it seems works fine.
Would you please elaborate a little more about "manually creating the user" - how the user is created, through the backend UI or through code?
Regards,
Svetoslav Manchev
Telerik
Thanks for your help Svetoslav.
I partially managed to solve the problem yesterday. It was an error on my part.
I'm modifying the sample to work with a web service instead of a database. So I made a mistake Generating the user object.
But my problems are not over... :(
Once I succesfully logged a user in the site, this error appears
"There is no configuration for data provider with the name of "WebApiMembershipProvider" for "Telerik.Sitefinity.Security.UserManager" manager. Please check the spelling of the name and whether such configuration exists. "
And now I can't even go to the login page. Every time I try it this error appears (While loading the login page).
What am I missing?
Hi Lucas,
Please check once again all the steps in the tutorial and take a special attention of its registration described here.
In case the issue still persists, would you please attache your code here or open a support ticket and to upload it there in order to inspect it in details.
Regards,
Svetoslav Manchev
Telerik
Well, I checked all the configuration and the error persists.
I'm attaching the error page and the code from my provider class. If anyone gets any idea...
And just to be clear:
Regards
using
System;
using
System.Collections.Generic;
using
System.Configuration.Provider;
using
System.Linq;
using
System.Web.Security;
using
Telerik.Newtonsoft.Json;
using
Telerik.Newtonsoft.Json.Linq;
using
Telerik.Sitefinity.Data;
using
Telerik.Sitefinity.Model;
using
Telerik.Sitefinity.Security;
using
Telerik.Sitefinity.Security.Data;
using
Telerik.Sitefinity.Security.Model;
using
WebAgent.FrontEndSite.Helpers;
namespace
WebAgent.FrontEndSite.Providers
public
class
WebApiMembershipProvider : MembershipDataProvider
private
readonly
ManagerInfo _managerInfo;
public
WebApiMembershipProvider()
:
base
()
ProviderName =
"WebApiMembershipProvider"
;
_managerInfo =
new
ManagerInfo
ProviderName = ProviderName,
ManagerType =
"Telerik.Sitefinity.Security.UserManager"
,
ApplicationName =
"/WebApiMembershipProvider"
;
#region Properties
public
string
ProviderName
get
;
set
;
public
override
string
ApplicationName
get
return
ManagerInfo.ApplicationName;
private
ManagerInfo ManagerInfo
get
return
_managerInfo;
public
override
ProviderAbilities Abilities
get
var abilities =
new
ProviderAbilities ProviderName = Name, ProviderType = GetType().FullName ;
abilities.AddAbility(
"GetUser"
,
true
,
true
);
abilities.AddAbility(
"CreateUser"
,
true
,
true
);
abilities.AddAbility(
"DeleteUser"
,
true
,
true
);
abilities.AddAbility(
"UpdateUser"
,
true
,
true
);
abilities.AddAbility(
"ValidateUser"
,
true
,
true
);
abilities.AddAbility(
"ResetPassword"
,
true
,
true
);
abilities.AddAbility(
"GetPassword"
,
false
,
false
);
return
abilities;
#endregion
#region Methods
protected
override
object
CreateNewTransaction(
string
transactionName)
return
new
object
();
public
override
void
CommitTransaction()
protected
override
void
Dispose(
bool
disposing)
if
(!disposing)
base
.Dispose(
false
);
public
override
bool
EmailExists(
string
email)
var parameters =
new
Dictionary<
string
,
string
>
"mail"
, email ;
var result = ServiceClient.Get(
"api/Security/MailExists"
, parameters);
return
result ==
"true"
;
public
override
bool
UserExists(
string
userName)
var parameters =
new
Dictionary<
string
,
string
>
"username"
, userName ;
var result = ServiceClient.Get(
"api/Security/UsernameExists"
, parameters);
return
result ==
"true"
;
private
User GetSitefinityUser(JObject jsonUser)
var user =
new
User
ApplicationName = ApplicationName,
IsBackendUser = jsonUser[
"field_IsBackendUser"
].Value<
bool
>(),
Id =
new
Guid(jsonUser[
"field_Id"
].Value<
string
>()),
Email = jsonUser[
"field_Mail"
].Value<
string
>(),
Comment = jsonUser[
"field_Comment"
].Value<
string
>(),
LastActivityDate = DateTime.UtcNow.AddDays(-1),
LastModified = DateTime.MinValue,
LastLoginDate = DateTime.MinValue,
FailedPasswordAnswerAttemptWindowStart = DateTime.MinValue,
FailedPasswordAttemptWindowStart = DateTime.MinValue,
Password = jsonUser[
"field_Password"
].Value<
string
>() + jsonUser[
"field_Salt"
].Value<
string
>(),
Salt = jsonUser[
"field_Salt"
].Value<
string
>(),
ManagerInfo = ManagerInfo,
IsApproved = jsonUser[
"field_IsApproved"
].Value<
bool
>(),
//PasswordFormat = 2
;
user.SetUserName(jsonUser[
"field_UserName"
].Value<
string
>());
user.SetCreationDate(DateTime.Now);
user.SetIsLockedOut(jsonUser[
"field_IsLockedOut"
].Value<
bool
>());
user.SetLastLockoutDate(DateTime.Now);
user.SetLastPasswordChangedDate(DateTime.Now);
user.SetPasswordQuestion(jsonUser[
"field_Question"
].Value<
string
>());
return
user;
public
override
IQueryable<User> GetUsers()
throw
new
NotSupportedException();
public
override
User GetUser(Guid id)
var parameters =
new
Dictionary<
string
,
string
>
"id"
, id.ToString() ;
var result = ServiceClient.Get(
"api/Security/GetUserById"
, parameters);
var user = JsonConvert.DeserializeObject<JObject>(result);
return
GetSitefinityUser(user);
public
override
User GetUserByEmail(
string
email)
var parameters =
new
Dictionary<
string
,
string
>
"mail"
, email ;
var result = ServiceClient.Get(
"api/Security/GetUserByMail"
, parameters);
var user = JsonConvert.DeserializeObject<JObject>(result);
return
GetSitefinityUser(user);
public
override
User GetUser(
string
userName)
var parameters =
new
Dictionary<
string
,
string
>
"username"
, userName ;
var result = ServiceClient.Get(
"api/Security/GetUserByLogin"
, parameters);
var user = JsonConvert.DeserializeObject<JObject>(result);
return
GetSitefinityUser(user);
public
override
User CreateUser(
string
username,
string
password,
string
email,
string
passwordQuestion,
string
passwordAnswer,
bool
isApproved,
object
providerUserKey,
out
MembershipCreateStatus status)
// First we validate the given parameters
if
(!ValidateParameters(
ref
username,
ref
password,
ref
email,
ref
passwordQuestion,
ref
passwordAnswer,
ref
providerUserKey,
out
status))
return
null
;
// Generate a new Salt
var str = GenerateSalt();
// Get a UTC DateTime
var utcNow = DateTime.UtcNow;
// Create a new user with the parameters entered from the Sitefinity backend
var newUser = CreateUser((Guid)providerUserKey, username);
newUser.Password = EncodePassword(password, str, PasswordFormat);
newUser.PasswordAnswer = EncodePassword(passwordAnswer.ToUpperInvariant(),
null
, PasswordFormat);
newUser.Salt = str;
newUser.Email = email;
newUser.Comment =
string
.Empty;
newUser.IsApproved = isApproved;
newUser.FailedPasswordAttemptCount = 0;
newUser.FailedPasswordAttemptWindowStart = utcNow;
newUser.FailedPasswordAnswerAttemptCount = 0;
newUser.FailedPasswordAnswerAttemptWindowStart = utcNow;
newUser.PasswordFormat = (
int
)PasswordFormat;
newUser.PasswordAnswer = passwordAnswer;
newUser.SetPasswordQuestion(passwordQuestion);
newUser.SetCreationDate(DateTime.Now);
// Add the new user to the Switch in our custom database
var user =
new
JObject
"username"
,username,
"firstname"
,
""
,
"lastname"
,
""
,
"password"
,password,
"email"
,email
;
ServiceClient.Post(
"api/NewUser"
, user);
// Return our new Sitefinity User
return
newUser;
public
override
User CreateUser(
string
userName)
return
CreateUser(GetNewGuid(), userName);
public
override
User CreateUser(Guid id,
string
userName)
if
(id == Guid.Empty)
throw
new
ArgumentNullException(
"id"
);
if
(!
string
.IsNullOrEmpty(userName))
LoginUtils.CheckParameter(userName,
true
,
true
,
true
, 256,
"userName"
);
if
(UserExists(userName))
throw
new
ProviderException(
"Username already exists."
);
var user =
new
User ApplicationName = ApplicationName, Id = id ;
user.SetUserName(userName);
((IDataItem)user).Provider =
this
;
user.ManagerInfo = ManagerInfo;
return
user;
public
override
bool
ValidateUser(
string
userName,
string
password)
return
ValidateUser(GetUser(userName), password);
public
override
bool
ValidateUser(User user,
string
password)
if
(user ==
null
)
return
false
;
var flag = CheckValidPassword(user, password);
if
(flag)
user.LastLoginDate = DateTime.UtcNow;
user.FailedPasswordAttemptWindowStart = DateTime.UtcNow;
user.FailedPasswordAttemptCount = 0;
else
UpdateFailureCount(user,
"password"
);
return
flag;
private
bool
CheckValidPassword(User user,
string
password)
var parameters =
new
Dictionary<
string
,
string
>
"grant_type"
,
"password"
,
"username"
,user.UserName,
"password"
,password
;
try
var token = ServiceClient.Post(
"Token"
, parameters).Length > 0;
return
true
;
catch
(Exception)
return
false
;
public
override
void
Delete(User item)
throw
new
NotSupportedException();
public
override
string
GetPassword(User user,
string
answer)
throw
new
NotSupportedException();
public
override
string
GetPassword(
string
userName,
string
answer)
throw
new
NotSupportedException();
public
override
string
GetPassword(Guid userId,
string
answer)
return
GetPassword(GetUser(userId), answer);
public
override
string
ResetPassword(User user,
string
answer)
throw
new
NotSupportedException();
public
override
string
ResetPassword(
string
userName,
string
answer)
throw
new
NotSupportedException();
public
override
string
ResetPassword(Guid userId,
string
answer)
throw
new
NotSupportedException();
#endregion
#region Not Supported
public
override
bool
ChangePassword(Guid userId,
string
oldPassword,
string
newPassword)
throw
new
NotSupportedException();
public
override
bool
ChangePassword(
string
userName,
string
oldPassword,
string
newPassword)
throw
new
NotSupportedException();
public
override
bool
ChangePassword(User user,
string
oldPassword,
string
newPassword)
throw
new
NotSupportedException();
public
override
bool
ChangePasswordQuestionAndAnswer(User user,
string
password,
string
newPasswordQuestion,
string
newPasswordAnswer)
throw
new
NotSupportedException();
public
override
bool
ChangePasswordQuestionAndAnswer(Guid id,
string
password,
string
newPasswordQuestion,
string
newPasswordAnswer)
throw
new
NotSupportedException();
public
override
bool
ChangePasswordQuestionAndAnswer(
string
userName,
string
password,
string
newPasswordQuestion,
string
newPasswordAnswer)
throw
new
NotSupportedException();
public
override
System.Collections.IEnumerable GetItems(Type itemType,
string
filterExpression,
string
orderExpression,
int
skip,
int
take,
ref
int
? totalCount)
throw
new
NotSupportedException();
public
override
bool
UnlockUser(Guid userId)
throw
new
NotSupportedException();
public
override
bool
UnlockUser(
string
userName)
throw
new
NotSupportedException();
public
override
bool
UnlockUser(User user)
throw
new
NotSupportedException();
#endregion
Hello Lucas,
Would you please check in the SecurityConfig.config (~/App_Data/Sitefinity/Configuration/) is there registration of your custom membership provider with the same name as in the error: WebApiMembershipProvider, in the list of the " <membershipProviders>" providers.
This settings are in the backend: Administration > Settings > Advanced > Security > Membership Providers > (your provider)
In the example I have tested the settings are: http://screencast.com/t/cMLamuAB.
Regards,
Svetoslav Manchev
Telerik
I'm attaching a screenshot of the configuration page in the backend and the fragment of the config:
<
membershipProviders
>
<
add
title
=
"WebApi"
description
=
"Web API service"
type
=
"WebAgent.FrontEndSite.Providers.WebApiMembershipProvider, WebAgent.FrontEndSite"
ApplicationName
=
"/WebApiMembershipProvider"
maxInvalidPasswordAttempts
=
"5"
enablePasswordReset
=
"True"
minRequiredNonalphanumericCharacters
=
"0"
minRequiredPasswordLength
=
"7"
newPasswordLength
=
"8"
passwordAttemptWindow
=
"10"
passwordFormat
=
"Clear"
requiresQuestionAndAnswer
=
"False"
requiresUniqueEmail
=
"True"
enablePasswordRetrieval
=
"False"
enabled
=
"True"
name
=
"WebApi"
/>
</
membershipProviders
>
Hi Lucas,
Please try to change the name of the provide as in the provide type (WebApiMembershipProvider) and restart the application.
Regards,
Svetoslav Manchev
Telerik
Yup. That Works.
In the provider tag, the "name" attribute must be the same as the class name. I changed that and solved the problem.
Thanks!