There appears to be an issue with iOS 11 (still an issue as of iOS 11.0.1), home screen web apps, and cookies. When a cookie is set from the server, iOS 11 seems to intermittently delete the cookie. Other times, when the server expires a cookie, iOS 11 appears to intermittently fail to delete the cookie. These two behaviors occur after closing and re-opening the home screen web app and don't seem to be reproducible in Safari. These behaviors also don't appear to be present in iOS 10 or iOS 9.
I am guessing that this is a bug in iOS 11, but was wondering if anyone else has happened upon this issue and has figured out how to work around it.
For example, this ASP.NET code creates and expires a cookie upon login and logout. When the Index URL is added to the home screen, and the app is closed and re-opened (i.e., the Index URL gets reloaded) after either logging in or logging out, the app will sometimes show in the incorrect state upon reloading (logged out after logging in, or still logged in after logging out).
public class HomeController : Controller
{
[Route("~/")]
public ActionResult Index()
{
var loginCookie = Request.Cookies["Login"];
if (loginCookie != null)
{
return View();
}
else
{
return Redirect("Login");
}
}
[Route("~/login/")]
public ActionResult Login()
{
return View();
}
[HttpPost]
[Route("~/login/")]
public ActionResult LoginComplete()
{
var authCookie = new HttpCookie("Login", "login token")
{
Expires = DateTime.Now.AddMinutes(20)
};
Response.Cookies.Add(authCookie);
return Redirect("~/");
}
[Route("~/logout/")]
public ActionResult Logout()
{
var authCookie = new HttpCookie("Login", "login token")
{
Expires = DateTime.Now.AddMinutes(-1000)
};
Response.Cookies.Add(authCookie);
return View();
}
}
Here is the full repo if anyone wants to try reproducing the issue. Keep in mind that this is an intermittent issue, and multiple attempts may be required.
Related
My website (Spring MVC) allows users to sign up by using their Facebook accounts and sign into my site later with their Facebook accounts. I use scribejava (version 6.6.3) (https://github.com/scribejava/scribejava) for the Oauth integration with Facebook.
I have tested a use case and am unable to find a way to resolve it. Here is the list of steps:
The tester goes to my site's "Log in" page, clicks "Log in with Facebook", grants permissions at Facebook, gets redirected to my site, and signs out. This is a normal and successful flow.
The tester sign into Facebook at Facebook.com
The tester goes to Settings->Apps and Websites and removes my site
The tester goes to my site's "Log in" page, clicks "Log in with Facebook" button, gets redirected to Facebook, and sees an error message.
At step 4, the tester always gets an error message at the Facebook site instead of asking the tester to grant permissions again. See the following screenshot:
I cannot find a way at Facebook to remove this message when clicking on the "Log in with Facebook" button. Here is my code for the web interface. Did I miss something?
#RequestMapping( value="/facebook", method = RequestMethod.GET)
public void facebook(HttpServletRequest request,
#RequestParam(value = "page", required = true) String page,
HttpServletResponse response) throws Exception {
try {
OAuth20Service service = new ServiceBuilder(config.getProperty("facebook.clientId"))
.apiSecret(config.getProperty("facebook.clientSecret"))
.callback(getCallback())
.build(FacebookApi.instance());
String authUrl = service.getAuthorizationUrl();
response.sendRedirect(authUrl);
} catch (Exception e) {
response.sendRedirect("/oauthFail");
}
}
#RequestMapping( value="/facebook/callback", method = RequestMethod.GET)
public void facebookCallback(HttpServletRequest servletRequest,
#RequestParam(value = "code", required = false) String code,
#RequestParam(value = "error", required = false) String error,
HttpServletResponse servletResponse
) throws Exception {
try {
OAuth20Service service = new ServiceBuilder(config.getProperty("facebook.clientId"))
.apiSecret(config.getProperty("facebook.clientSecret"))
.callback(getCallback())
.build(FacebookApi.instance());
OAuth2AccessToken accessToken = service.getAccessToken(code);
final OAuthRequest request = new OAuthRequest(Verb.GET, "https://graph.facebook.com/v3.2/me");
service.signRequest(accessToken, request);
final com.github.scribejava.core.model.Response response = service.execute(request);
String body = response.getBody();
JSONObject jObject = new JSONObject(body);
String email = jObject.getString("email");
//success. use the email to create an account or if the email address exists, direct a userto their account page
} catch (Exception e) {
response.sendRedirect("/oauthFail");
}
}
How to handle this situation? I feel either something is wrong is my code or scribejava has a framework issue. Or this is a Facebook specific issue?
I have just tested the case. Couldn't reproduce.
Did you try running this Example
https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FacebookExample.java
?
I think your problem can be with API versions logic in Facebook.
You can try to explicitly use the latest one .build(FacebookApi.customVersion("3.2"))
I followed the article Use cookie authentication without ASP.NET Core Identity and downloaded the sample from 2.x/Cookies.
Ran the sample in VS 2017. Opened the "contact" page as directed in the documentation and from code (that it is protected), signed in using the credentials authenticated in the code using simple string comparison, it signs in if debugged, which means it adds user principal with its claims in but redirects back to log-in page instead of the contact page.
ConfigureServices:
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddMvc()
.AddRazorPagesOptions(options =>
{
options.Conventions.AuthorizePage("/Contact");
})
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
#region snippet1
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options => options.ExpireTimeSpan = new System.TimeSpan(0, 10, 0));
#endregion
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
Configure
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
// Call UseAuthentication before calling UseMVC.
#region snippet2
app.UseAuthentication();
#endregion
app.UseMvc();
Authentication
#region snippet1
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, user.Email),
new Claim("FullName", user.FullName),
new Claim(ClaimTypes.Role, "Administrator"),
};
var claimsIdentity = new ClaimsIdentity(
claims, CookieAuthenticationDefaults.AuthenticationScheme, ClaimTypes.Name, ClaimTypes.Role);
var authProperties = new AuthenticationProperties
{
AllowRefresh = true,
// Refreshing the authentication session should be allowed.
ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(10),
// The time at which the authentication ticket expires. A
// value set here overrides the ExpireTimeSpan option of
// CookieAuthenticationOptions set with AddCookie.
IsPersistent = true,
// Whether the authentication session is persisted across
// multiple requests. Required when setting the
// ExpireTimeSpan option of CookieAuthenticationOptions
// set with AddCookie. Also required when setting
// ExpiresUtc.
//IssuedUtc = <DateTimeOffset>,
// The time at which the authentication ticket was issued.
//RedirectUri = <string>
// The full path or absolute URI to be used as an http
// redirect response value.
};
await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity),
authProperties);
#endregion
and then I redirect to the contacts page but brought back to the login page.
After making a test with this project, I could reproduce your issue with Chrome, it works with Edge.
For making it work with Chrome, you could turn to launchSettings.json and change the sslPort for iisExpress to 44344 instead of 0.
I need to set a cookie in a WKWebView on both iOS 10 an iOS 11. After reading this, I wrote different code for the 2 iOS versions, using a JavaScript script in iOS 10 and WKWebsiteDataStore in iOS 11.
While using iOS 10 I have no issue, with iOS 11 the web app sees the cookie I used in the previous session. I mean, if the first time I launch the app the cookie value is "A", the web app doesn't see the cookie; if I close the app and relaunch it with "B" as the new value of the cookie, the web app now reads "A" from the cookie. However, with the Web Inspector I see the right value of the cookie.
It seems that with the WKWebsiteDataStore the cookie is set too late; how can I solve this issue (without using the same code used in iOS 10 even in iOS 11)?
My code is:
let preferences = WKPreferences()
preferences.javaScriptEnabled = true
let configuration = WKWebViewConfiguration()
let userContentController = WKUserContentController()
configuration.preferences = preferences
if #available(iOS 11, *) {
if let cookie = cookie {
let dataStore = WKWebsiteDataStore.default()
dataStore.httpCookieStore.setCookie(cookie) {
configuration.websiteDataStore = dataStore
configuration.userContentController = userContentController
self.createAndLoadWebView(with: configuration)
}
}
} else {
if let cookie = cookie {
let script = getJSCookiesString(for: [cookie])
let cookieScript = WKUserScript(source: script, injectionTime: .atDocumentStart, forMainFrameOnly: false)
userContentController.addUserScript(cookieScript)
configuration.userContentController = userContentController
createAndLoadWebView(with: configuration)
}
}
And then:
private func createAndLoadWebView(with configuration: WKWebViewConfiguration) {
webView = WKWebView(frame: self.containerView.frame, configuration: configuration)
self.containerView.addSubview(webView)
let req = URLRequest(url: pageUrl)
webView.load(req)
}
Add same cookies in Request header, i faced same problem but after add cookies in header request page load normally.
I'm creating cookies in VertX and want to remove them again once a users logs out.
AccountController.handleLogin(vertx, router.post("/login"))
...
fun handleLogin(vertx: Vertx, route: Route) {
route.handler { rtx ->
rtx.request().bodyHandler { btx ->
vertx.executeBlocking<Login>({
it.complete(AccountController.login(Json.decodeValue(String(btx.bytes), Login::class.java)))
}, {
if (it.succeeded()) {
// set some cookies
rtx.addCookie(Cookie.cookie("atom-session", it.result().session).setHttpOnly(true).setSecure(secure))
That cookie can now be seen in Chrome:
When I want to remove that cookie again:
AccountController.handleLogout(vertx, router.post("/logout"))
...
fun handleLogout(vertx: Vertx, route: Route) {
route.handler { rtx ->
rtx.request().bodyHandler { btx ->
vertx.executeBlocking<Logout>({
val logout = Json.decodeValue(String(btx.bytes), Logout::class.java)
it.complete(AccountController.logout(logout))
}, {
if (it.succeeded()) {
log.info("Cookies Will No Be Removed ...")
rtx.removeCookie("atom-session")
log.info("DONE!")
I can see the messages being printed saying that cookies will be removed, but when I refresh the resources in Chrome, all the cookies that were set on login are still there. including atom-session
Am I doing this wrong or is this a bug in VertX ?
The removeCookie method will remove it from the request object but that does not remove a cookie from a web client. In order to force it to be removed from a client the cookie must be sent back with an expiration date. For example you should do:
rtx.getCookie("atom-session").setMaxAge(0)
This is not a vert.x feature per se, but how cookies work.
I have implemented an MVC Application running with Sitecore. The Startup class of OWIN have implemented following like that:
[assembly: OwinStartupAttribute(typeof(WebApplication1.Startup))]
namespace WebApplication1.Web
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
public void ConfigureAuth(IAppBuilder app)
{
app.CreatePerOwinContext<AppIdentityDbContext>(AppIdentityDbContext.Create);
app.CreatePerOwinContext<AppUserManager>(AppUserManager.Create);
app.CreatePerOwinContext<AppRoleManager>(AppRoleManager.Create);
app.UseOAuthBearerTokens(new OAuthAuthorizationServerOptions
{
Provider = new AppOAuthProvider(),
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/Authenticate")
});
}
}
}
I have expected when I submit a username, password and grant_type value is password with method POST into URL http://<>/Authenticate the token bearer return to allows user can log in. Unfortunately, the Sitecore throw content is not found and I cannot figure out the way let the request going to the OWIN Middle Authorization. How can I sort it out?