Persistent login in Sharepoint 2010 using SessionSecurityToken? - sharepoint

I have a public website which runs on Sharepoint. Now when the users login, we want them to be able to pick "Remember login" so they have a persistent login.
As far as I've understood, Sharepoint supports this. The only thing you have to do, is to run:
spFederationAuthenticationModule.SetPrincipalAndWriteSessionToken(someSessionSecurityToken, true);
Now, I don't know how to get the SessionSecurityToken that overload requires. Currently we use the one without true.
Our code:
MembershipProvider membershipProvider = Membership.Providers["CustomProvider"];
VerifyCredentialsResult verifyCredentialsDto = null;
using (WindowsIdentity.Impersonate(IntPtr.Zero))
{
var customerService = Kernel.Resolve<ICustomerServiceAgent>();
verifyCredentialsDto = customerService.VerifyCredentials(userName, passwordOrSsoToken);
Kernel.ReleaseComponent(customerService);
}
if (!verifyCredentialsDto.Succes)
return false;
SecurityToken securityToken =
SPSecurityContext.SecurityTokenForFormsAuthentication(new Uri(SPContext.Current.Web.Url),
"CustomProvider",
"CustomRoleProvider",
verifyCredentialsDto.CustomerId,
passwordOrSsoToken);
if (securityToken != null)
{
SPFederationAuthenticationModule spFederationAuthenticationModule =
SPFederationAuthenticationModule.Current;
spFederationAuthenticationModule.SetPrincipalAndWriteSessionToken(securityToken);
return true;
}
return false;
Implementing the parameter which is persistent true/false is easy, but how do we change our code to support persistent cookies when logging in?

Related

Connecting Azure Active Directory with .NET Web Api, authenticated is always false

I am developing a Standard .NET Web Api 2 with Angular 7 and I need to connect Azure Active Directory.
I have added this code:
public static void ConfigureAuth(IAppBuilder app)
{
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Tenant = configurationManager.AadTenant,
TokenValidationParameters = new TokenValidationParameters
{
ValidAudience = configurationManager.AadAudience,
},
});
}
Both my Tenant and Audience are correct. Everything works fine and the token is valid and exists in the request.
The problem is that IsAuthenticated is always false and when I looked inside the claims in the identity they were empty
protected override bool IsAuthorized(HttpActionContext actionContext)
{
return base.IsAuthorized(actionContext); // Always false
}
I don't know where the problem is. I have tried many links but none of them worked for me. Anybody knows why? Thanks
To protect your service, you can use IsAuthorize filter implementation something like below:
private static string trustedCallerClientId = ConfigurationManager.AppSettings["ida:TrustedCallerClientId"];
protected override bool IsAuthorized(HttpActionContext actionContext)
{
bool isAuthenticated = false;
try
{
string currentCallerClientId = ClaimsPrincipal.Current.FindFirst("appid").Value;
isAuthenticated = currentCallerClientId == trustedCallerClientId;
}
catch (Exception ex)
{
new CustomLogger().LogError(ex, "Invalid User");
isAuthenticated = false;
}
return isAuthenticated;
}
The principal is not taken from the current thread, but from the actionContext. So, what you must set is the principal in the request context of the action context:
actionContext.RequestContext.Principal = yourPrincipal;
I am assuming your action context.requestcontext does not have the right data, that''s why even though your request are succeeding but your attribute is always false.
Reference:
https://www.c-sharpcorner.com/article/azure-active-directory-authentication/
Hope it helps.

UWP App - Azure AD Updated Permission Scopes for Single Sign-On JWT Not Triggering Renewed User Consent

Summary:
Our Universal Windows App single-tenant client uses an ASP.NET Web API 2 as a proxy for single-sign on for various Microsoft Office 365 APIs. We use Active Directory for server authentication and the on-behalf-of single sign-on model in our server to exchange tokens for the Office 365 APIs.
Problem:
We have updated a permission scope in Azure for the Office 365 API and the user is not prompted to authorize permission for the new scope, nor is the new scope appearing on NEW tokens. What needs to be done to DETECT and ALLOW our users to authorize new permission scopes?
Additional Details:
Our server is hosted in MSFT Azure App Services. I understand the manifest in Azure is auto-generated and does not need to be manually updated to reflect the updated permission scope?
When the user first logs into the UWP app, they consent to single sign-on permissions associated with the server (eg. Mail.ReadWrite, etc.) which works fine. However, the user consent prompt does not show up again, even after I’ve removed both the client and server apps from my list of consented to apps using
We use the WebTokenRequest and WebAuthenticationCoreManager libraries in the client to get the token for the server. I have also tried using WebAuthenticationBroker (which is not the correct method for our sign-on architecture) and the ADAL library in our client. None of these libraries are prompting for the updated permission.
I have also tried adding wtf.Properties.Add("prompt", "consent"); to our WebTokenRequest to force the user to reapprove permissions. This does not work.
I have also tried restarting the App Service in Azure. This does nothing.
UPDATED 11/10/16:
Following is relevant code I've pulled from our app architecture which may help. Additionally, our server utilizes ADAL version 2.24.304111323.
In our UWP app:
public class AppAuth
{
WebTokenRequestResult result;
WebAccount acc;
async Task<WebTokenRequestResult> GetTokenAsync(WebTokenRequestPromptType promptType = WebTokenRequestPromptType.Default)
{
var wtr = new WebTokenRequest(
provider: "https://login.windows.net",
scope: "",
clientId: appClientId,
promptType: promptType
);
wtr.Properties.Add("authority", "https://login.windows.net");
wtr.Properties.Add("resource", azureWebsiteUrl);
if (promptType != WebTokenRequestPromptType.ForceAuthentication)
{
result = (acc == null) ?
await WebAuthenticationCoreManager.GetTokenSilentlyAsync(wtr) :
await WebAuthenticationCoreManager.GetTokenSilentlyAsync(wtr, acc);
}
if (promptType == WebTokenRequestPromptType.ForceAuthentication ||
result?.ResponseStatus == WebTokenRequestStatus.UserInteractionRequired)
{
result = (acc == null) ?
await WebAuthenticationCoreManager.RequestTokenAsync(wtr) :
await WebAuthenticationCoreManager.RequestTokenAsync(wtr, acc);
}
return result;
}
}
In our server:
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
TokenValidationParameters = new TokenValidationParameters
{
SaveSigninToken = true,
ValidateIssuer = false,
ValidAudience = ConfigurationManager.AppSettings["ida:Audience"]
}
});
}
}
public class TokenChange
{
protected AdUser _user;
private UserAssertion _assertion;
private static string _aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];
private static string _tenant = ConfigurationManager.AppSettings["ida:Tenant"];
private static string _clientId = ConfigurationManager.AppSettings["ida:ClientId"];
private static string _appKey = ConfigurationManager.AppSettings["ida:AppKey"];
private string _accessToken;
public AuthenticationResult AuthResult { get; set; }
public AdalException AuthException { get; set; }
private string _emailAddress;
private HttpClient _httpClient;
public bool Authenticate()
{
_accessToken = null;
if (ClaimsPrincipal.Current.Identity.IsAuthenticated)
{
var bootstrapContext = ClaimsPrincipal.Current.Identities.First().BootstrapContext
as System.IdentityModel.Tokens.BootstrapContext;
if (bootstrapContext != null)
{
Claim subject = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier);
var upn = ClaimsPrincipal.Current.FindFirst(ClaimTypes.Upn);
var email = ClaimsPrincipal.Current.FindFirst(ClaimTypes.Email);
var userName = upn != null ? upn.Value : email?.Value;
_emailAddress = ClaimsPrincipal.Current.Identity.Name;
var userNameClaim = ClaimsPrincipal.Current.FindFirst("name");
_fullName = userNameClaim != null ? userNameClaim.Value : String.Empty;
_accessToken = bootstrapContext.Token;
_assertion = new UserAssertion(_accessToken, "urn:ietf:params:oauth:grant-type:jwt-bearer", userName);
}
}
return _accessToken != null;
}
public bool GetAccess(string apiResource)
{
bool gotAccess = false;
AuthResult = null;
AuthException = null;
if (_accessToken != null || Authenticate())
{
ClientCredential clientCred = new ClientCredential(_clientId, _appKey);
string authority = String.Format(CultureInfo.InvariantCulture, _aadInstance, _tenant);
AuthenticationContext authContext = new AuthenticationContext(authority);
bool retry = false;
int retryCount = 0;
do
{
retry = false;
try
{
AuthResult = authContext.AcquireToken(apiResource, clientCred, _assertion);
}
catch (AdalException ex)
{
AuthException = ex;
if (ex.ErrorCode == "temporarily_unavailable")
{
retry = true;
retryCount++;
Thread.Sleep(500);
}
else
{
throw (ex);
}
}
} while ((retry == true) && (retryCount < 1));
if (AuthResult != null && AuthResult.AccessToken != null)
{
gotAccess = true;
}
}
return gotAccess;
}
Based on the description, you were developing an single tenant application which calling the downstream web API(Office 365 API) in your web API.
If you were using the cache to acquire the token in your web API, it will not acquire the new token unless the token is expired. And in this scenario, there is no need to consent/reconsent to update the permission.
Please ensure that you web API is acquire the token from new request instead of cache. If you were using the DbTokenCache, you can clear the cache by deleting the token cache records in PerWebUserCaches table in the database.
Note
In the describing scenario above, since the downstream web API(Office 365 API) get the token using the token issued for your web API which require users sign-in. So only the delegated permission work in the scenario( scp claim in the token instead of roles).

How to get HttpContext in servicestack.net

I am unable to switch to all of service stack's new providers, authentication, etc. So, I am running a hybrid scenario and that works great.
To get the current user in service, I do this:
private IPrincipal CurrentUser()
{
var context = HttpContext.Current;
if (context != null)
{
var user = context.User;
if (user != null)
{
if (!user.Identity.IsAuthenticated)
return null;
return user;
}
}
return null;
}
Is there an alternative/better way to get the current http context directly from a service? I would prefer to not have to use the HttpContext.Current if I do not have to?
This is alternative way... It goes through ServiceStack to get the OriginalRequest which will be an ASP.NET request (though could be HttpListener HttpRequest if not used within ASP.NET application). Not sure if it's better but you no longer have HttpContext.Current within your Service code.
public class MyService : Service
{
public object Get(MyRequest request)
{
var originalRequest = this.Request.OriginalRequest as System.Web.HttpRequest;
var user = originalRequest.RequestContext.HttpContext.User;
// more code...
}
}

Orchard CMS reseting the Culture

I recently watched the very useful orchard harvest video on localization and internationalization, by Piotr Szmyd
I want to set the culture using this method, checking for a cookie
public class CultureSelector : ICultureSelector
{
public const int SelectorPriority = 5;
public const string CookieName = "Riders-Location-Cookie";
public const string CookieValueName = "location-code";
public CultureSelectorResult GetCulture(HttpContextBase context)
{
if (context == null || context.Request == null || context.Request.Cookies == null)
{
return null;
}
// check for a cookie
var cookie = context.Request.Cookies[CookieName];
if (cookie != null && !string.IsNullOrEmpty(cookie.Values[CookieValueName]))
{
return new CultureSelectorResult { Priority = SelectorPriority, CultureName = cookie.Values[CookieValueName] };
}
return null;
}
}
That works, However, I do want to user to be able to reset their own culture on the site. How do I reset the culture for the entire site when the user chooses to.
Lets say for instance if I have a select list that is output as part of a custom module.
I've looked at the ChangeCulture code form the Orchard CulturePicker Module but this dosen't seem to change to culture for the entier site as setting it with an implementation of ICultureSelector would.
If I correctly understand, you'd like a user to be able to change his current culture and/or be able to return to the default site culture, right?
In your case it should be as easy as either changing the cookie value or removing it (to set default culture) as a response to some user action.

Checking permissions of a user with a site collection

I want to check whether a user has permissions to a site collection. But i dono how to use SPSite.DoesUserHavePermissions().
What is SPReusableAcl? How can i get it for checking the permissions of the user?
Doesn't the MSDN article (SPWeb.DoesUserHavePermissions Method (String, SPBasePermissions)) help you? The example code can be used to check whether the user has access to a site collection:
using System;
using Microsoft.SharePoint;
namespace Test
{
class Program
{
static void Main(string[] args)
{
using (SPSite site = new SPSite("http://localhost"))
{
using (SPWeb web = site.OpenWeb())
{
// Make sure the current user can enumerate permissions.
if (web.DoesUserHavePermissions(SPBasePermissions.EnumeratePermissions))
{
// Specify the permission to check.
SPBasePermissions permissionToCheck = SPBasePermissions.ManageLists;
Console.WriteLine("The following users have {0} permission:", permissionToCheck);
// Check the permissions of users who are explicitly assigned permissions.
SPUserCollection users = web.Users;
foreach (SPUser user in users)
{
string login = user.LoginName;
if (web.DoesUserHavePermissions(login, permissionToCheck))
{
Console.WriteLine(login);
}
}
}
}
}
Console.ReadLine();
}
}
}
In the sample code above you would just have to change your Site URL and the Variable permissionToCheck. SPBasePermissions has a lot of possible permissions to check against, you can see the enumeration here (SPBasePermissions Enumeration).
Actually there are a lot of tutorials on how to check some user's permissions and you are not limited to DoesUserHavePermissions, see the following Google Search.
As usual, the MSDN examples provide nice textbook examples that do not always apply to real-life scenarios.
In the context of an application page running on SharePoint 2010, from what i understand this code needs to be wrapped in a call to RunWithElevatedPrivileges and even then, as my comment implies, it seems there is an implied catch-22 in the requirements. This works for me (the LoginName is just the FBA username or "domain\user" for AD user for the site - in our case an e-mail address is used):
SPSecurity.RunWithElevatedPrivileges(delegate()
{
using (SPSite elevatedSite = new SPSite(siteCollectionUrl))
{
foreach (SPSite siteCollection in elevatedSite.WebApplication.Sites)
{
using (SPWeb elevatedWeb = siteCollection.OpenWeb())
{
bool allowUnsafeUpdates = elevatedWeb.AllowUnsafeUpdates;
bool originalCatchValue = SPSecurity.CatchAccessDeniedException;
SPSecurity.CatchAccessDeniedException = false;
try
{
elevatedWeb.AllowUnsafeUpdates = true;
// You can't verify permissions if the user does not exist and you
// can't ensure the user if the user does not have access so we
// are stuck with a try-catch
SPUser innerUser = elevatedWeb.EnsureUser(loginName);
if (null != innerUser)
{
string splogin = innerUser.LoginName;
if (!string.IsNullOrEmpty(splogin) && elevatedWeb.DoesUserHavePermissions(splogin, SPBasePermissions.ViewPages))
{
// this user has permissions; any other login - particularly one that
// results in an UnauthorizedAccessException - does not
}
}
}
catch (UnauthorizedAccessException)
{
// handle exception
}
catch (Exception)
{
// do nothing
}
finally
{
elevatedWeb.AllowUnsafeUpdates = allowUnsafeUpdates;
// reset the flag
SPSecurity.CatchAccessDeniedException = originalCatchValue;
}
}
}
}
});
SPSite.DoesUserHavePermissions(SPReusableAcl, SPBasePermissions);

Resources