Azure Mobile Service Single Sign on Microsoft Account - azure-mobile-services

I have implemented single sign on using the WL api, but I only recently realized that I need to call mobileService.login on top of that to use the nice authentication features of Azure Mobile Services.
I followed this tutorial
http://www.windowsazure.com/en-us/develop/mobile/tutorials/single-sign-on-windows-8-js/#add-authentication
and added this piece of code:
var login = function () {
return new WinJS.Promise(function (complete) {
WL.init();
WL.login({ scope: ["wl.signin", "wl.basic", "wl.birthday", "wl.emails"] }).then(function (result) {
session = result.session;
WinJS.Promise.join([
WL.api({ path: "me", method: "GET" }),
mobileService.login("microsoftaccount", session.authentication_token)
]).done(function (results) {
var profile = results[0];
var mobileServicesUser = results[1];
var title = "Welcome " + profile.first_name + "!";
var message = "You are now logged in as: " + mobileServicesUser.userId;
var dialog = new Windows.UI.Popups.MessageDialog(message, title);
dialog.showAsync().done(complete);
});
}, function (error) {
session = null;
var dialog = new Windows.UI.Popups.MessageDialog("You must log in.", "Login Required");
dialog.showAsync().done(complete);
});
});
}
however on this line
mobileService.login("microsoftaccount", session.authentication_token)
my session.authentication_token is undefined. (I have an access_token)
If I don't pass the token, I am prompted to sign in every time I launch the app, which defeats the purpose of the integrated sign on.
Any ideas?

To get the authentication token, you need to pass the redirect URI to the call to WL.init:
WL.init({
redirect_uri: "<< INSERT REDIRECT DOMAIN HERE >>"
});
Where the redirect domain must be the same as the one in your Live Connect application.

Related

Identity server stuck in redirect loop

I'm using identityserver4 for single sign-on. For most of the time application function smoothly but intermittently we face a redirect loop issue which becomes a show stopper for us until we restart's our app service. The page goes on loading continuously before finally showing a 'Bad request - Request Too Long' page with message: HTTP Error 400. The size of the request headers is too long. If we check the network tab, we can see that the application is looping between the identity server and client application redirect sign in pages. The application insight tells us that the client app gives a 401 on his home/index page and then a 302 on the signin-oidc url, then goes to the identity server connect/token, then connect/userinfo endpoints to get claims and comes back to the client home/index page to again get a 401. The loop continues (Identity server says user is authenticated while client says it is not). We are unable to find a fix for this since long. Any help is appreciated. Attaching the client side configuration for reference.
Findings
Our client app is an mvc app & we have used Session's & TempData in few area's. This areas are the triggering point of the redirect issue. What we have observed is, when the client initially login the authentication cookie is created (Cookie Name: AudDiscoveryAuth) and I could see it being passed in header for each request made to the controller actions. But once the user visit's any such area where we have used Session/TempData and Log out or any other user tries to login, Identity server successfully authenticates the user also the userendpoint to retrieve the details is being invoked however the cookie itself is not being created and is missing in every request to the Index/Home action method hence the redirect loop. Wondering what could be hampering in issuing cookie when using session variable elsewhere in the application or is their a setting missing.
Also in every redirect the occurrence of OpenIdConnect.nonce.XXX cookie is incremented. Once the count of OpenIdConnect.nonce.XXX reaches more then a certain level we get the bad request error page
public void Configuration(IAppBuilder app)
{
string baseClientAddress = ConfigurationManager.AppSettings["ApplicationUrl"];
int slidingExpiryHrs = Convert.ToInt32(ConfigurationManager.AppSettings["SlidingExpiryHrs"]);
int slidingExpiryMins = Convert.ToInt32(ConfigurationManager.AppSettings["SlidingExpiryMins"]);
TimeSpan expireTimeSpan = new TimeSpan(slidingExpiryHrs, slidingExpiryMins, 0);
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap = new Dictionary<string, string>();
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationType,
CookieName = "AudDiscoveryAuth",
ExpireTimeSpan = expireTimeSpan,
SlidingExpiration = true
});
app.UseOpenIdConnectAuthenticationPatched(new OpenIdConnectAuthenticationOptions
{
ClientId = "ratingspro.web",
Authority = IdsvrConstants.BaseAddress,
RedirectUri = baseClientAddress + "signin-oidc/",
PostLogoutRedirectUri = baseClientAddress + "signout-callback-oidc/",
ResponseType = "code id_token",
Scope = "openid api1 ratingspro.webapi offline_access",
UseTokenLifetime = false,
SignInAsAuthenticationType = DefaultAuthenticationType,
RequireHttpsMetadata = true,
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = async n =>
{
var client = HttpClientFactory.Create();
var tokenResponse = await client.RequestAuthorizationCodeTokenAsync(new AuthorizationCodeTokenRequest
{
Address = IdsvrConstants.TokenEndpoint,
ClientId = "ratingspro.web",
ClientSecret = "secret",
Code = n.Code,
RedirectUri = n.RedirectUri,
});
if (tokenResponse.IsError)
{
LogHelper.LogMessage("RatingsproApp: Startup => tokenResponseError: " + tokenResponse.Error);
throw new AuthenticationException(tokenResponse.Error);
}
var userInfoResponse = await client.GetUserInfoAsync(new UserInfoRequest
{
Address = IdsvrConstants.UserInfoEndpoint,
Token = tokenResponse.AccessToken
});
if (userInfoResponse.IsError)
{
throw new AuthenticationException(userInfoResponse.Error);
}
var claims = userInfoResponse.Claims;
if (claims.Any(c => c.Type == "ApplicationAccessDenied"))
{
throw new AuthenticationException(claims.FirstOrDefault(c => c.Type == "ApplicationAccessDenied").Value);
}
// create new identity
var id = new ClaimsIdentity(n.AuthenticationTicket.Identity.AuthenticationType);
id.AddClaims(claims);
id.AddClaim(new Claim("AccessToken", tokenResponse.AccessToken));
id.AddClaim(new Claim("expires_at", DateTime.Now.AddSeconds(tokenResponse.ExpiresIn).ToLocalTime().ToString(CultureInfo.InvariantCulture)));
id.AddClaim(new Claim("refresh_token", tokenResponse.RefreshToken));
id.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));
client.Dispose();
n.AuthenticationTicket = new AuthenticationTicket(
new ClaimsIdentity(id.Claims, n.AuthenticationTicket.Identity.AuthenticationType, "name", "role"),
n.AuthenticationTicket.Properties);
},
RedirectToIdentityProvider = n =>
{
if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.Logout)
{
var idTokenHint = n.OwinContext.Authentication.User.FindFirst("id_token").Value;
n.ProtocolMessage.IdTokenHint = idTokenHint;
}
return Task.FromResult(0);
},
AuthenticationFailed = x =>
{
x.ProtocolMessage.RedirectUri = "/";
return Task.CompletedTask;
}
}
});
}
}

Ionic user token

I want to add a push token to an user in my application. I have the push token, I have the user, but I can't add the token to the user. How I can add the push token to this user?
Here is the code:
var io = Ionic.io();
username = localStorage.getItem('username');
var signupSuccess = function(user) {
// The user was authenticated; you can get the authenticated user
console.log(user);
};
var signupFailure = function(errors) {
for (var err in errors) {
// Check the error and provide an appropriate message
// for your application.
user = Ionic.User.current();
}
};
var details = {
'email': 'email#gmail.com',
'password': 'pass2',
'username': 'username'
}
Ionic.Auth.signup(details).then(signupSuccess, signupFailure);
var push = new Ionic.Push();
var user = Ionic.User.current();
var callback = function(pushToken) {
alert('TOKEN: ' + pushToken.token);
user.addPushToken(pushToken);
user.save(); // You NEED to call a save after you add the token
}
push.register(callback);
It's no longer user.addPushToken(pushToken);
Instead you need:
push.register(function(token) {
push.saveToken(token);
});
This will automatically add the token to the currently logged in Ionic user.
See this example from Ionic documentation
N.B. as you're adding this at the point that the user is signing in, you also need to add the above code to register for push inside your $ionicPlatform.ready function:
$ionicPlatform.ready(function() {
var push = new Ionic.Push();
push.register(function(token) {
push.saveToken(token);
});
});
Or more likely, create one function to register for push which is called both from within $ionicPlatform.ready and also from within your signup / signin functions.
Otherwise, if Push has not been registered inside $ionicPlatform.ready, it won't add the token to your user when you call push.saveToken after signup / signin.
I can suggest one thing. You can ask the backend or server to team add a user token in the database or else.
You can manually add the following thing if it is an JSON object:
user["token"]=value

can't get gmail user displayname via firebase3 login in Ionic platform

I am working on google auth with firebase signinwithcredential. I first used cordovaoath to get the id token and sign it in with firebase. I specifically indicated in my code that I would like to get the displayName, but it always come back null. I was wondering anyone have faced the same problem?
this.loginWithGoogle = function loginWithGoogle() {
$cordovaOauth.google("mygoogleclient id here", ["https://www.googleapis.com/auth/calendar","https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/userinfo.profile","https://www.googleapis.com/auth/plus.me", "https://www.googleapis.com/auth/plus.login", "https://www.googleapis.com/auth/drive"]).then(function (result) {
// "email", "profile",
console.log("first come here to oath");
console.log("Response Object -> " + JSON.stringify(result));
var unsubscribe = firebase.auth().onAuthStateChanged(function (firebaseUser) {
unsubscribe();
// Check if we are already signed-in Firebase with the correct user.
// Build Firebase credential with the Google ID token.
var credential='';
credential = firebase.auth.GoogleAuthProvider.credential(
result.id_token);
// Sign in with credential from the Google user.
firebase.auth().signInWithCredential(credential).catch(function (error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// The email of the user's account used.
var email = error.email;
// The firebase.auth.AuthCredential type that was used.
var credential = error.credential;
});
});
}, function (error) {
console.log("Error -> " + error);
});
};
sreenshot of my returned result
You should request an access token, and not an ID token, if you are requesting additional scopes.
To fix your code, change this line:
var credential = firebase.auth.GoogleAuthProvider.credential(
null, result.access_token); // <- access_token

No 'Access-Control-Allow-Origin', only errors on first call but works subsequently

I have an AngularJS app which is trying to auth with my Web Api. I receive the below error during the first call to my server if the user does not exist in my database, but does not happen on subsequent calls to the same method once the user exists in my db. (relevant code at the bottom)
No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:1378' is therefore not allowed access. The response had HTTP status code 500.
The flow of the logic is:
AngularJS auths with Facebook when the user clicks login
App does an $http.post to my server for auth/login passing their credentials
Server polls Facebook API for user details
If user exists, update their profile and auth 'em
Else, create new membership user, update with FB details, and auth 'em
The only thing that's different if they don't exist in the database (which is when the defect occurs) is that the login method asynchronously calls a createUser method then returns data. No additional external calls are made.
API startup method enabling CORS:
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
var cors = new EnableCorsAttribute("*","*","*");
config.EnableCors(cors);
ConfigureOAuth(app);
app_start.WebApiConfig.Register(config);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config);
}
API Controller:
[Route("Login")]
[HttpPost]
[AllowAnonymous]
public async Task<FacebookUserModel> Login(FacebookUserRequest user)
{
FacebookUserModel fbUser = new FacebookUserModel();
// Build FacebookUser object
try {
// Grab basic user details
string profileRequestUri = "https://graph.facebook.com/" + user.fbID + "?access_token=" + user.access_token;
HttpWebRequest profileRequest = (HttpWebRequest)WebRequest.Create(profileRequestUri);
profileRequest.Method = WebRequestMethods.Http.Get;
profileRequest.Accept = "application/json";
HttpWebResponse profileResponse = (HttpWebResponse)profileRequest.GetResponse();
Stream profileResponseStream = profileResponse.GetResponseStream();
StreamReader profileStreamReader = new StreamReader(profileResponseStream);
fbUser = JsonConvert.DeserializeObject<FacebookUserModel>(profileStreamReader.ReadToEnd());
} catch (Exception) ...
try {
// Grab profile picture
string pictureRequestUri = "https://graph.facebook.com/" + user.fbID + "/picture";
HttpWebRequest pictureRequest = (HttpWebRequest)WebRequest.Create(pictureRequestUri);
pictureRequest.Method = WebRequestMethods.Http.Get;
HttpWebResponse pictureResponse = (HttpWebResponse)pictureRequest.GetResponse();
fbUser.profilePictureUri = pictureResponse.ResponseUri.ToString();
} catch (Exception) ...
// If user exists, change password to new token and return)
if(userExists)
{
try {
IdentityUser identityUser = _repo.FindUser(ID, pass).Result;
FacebookUserModel dbUser = db.FacebookUserObjects.First(u => u.identityUserID == identityUser.Id);
db.Entry(dbUser).CurrentValues.SetValues(fbUser);
db.SaveChangesAsync();
fbUser.identityUserID = identityUser.Id;
return fbUser;
}
catch (Exception e)
{ return null; }
}
// Else, create the new user using same scheme
else
{
UserModel newUser = new UserModel
{
UserName = ID,
Password = pass,
ConfirmPassword = pass
};
// Create user in Identity & linked Facebook record
createUser(newUser, fbUser);
return fbUser;
}
}
private async void createUser(UserModel newUser, FacebookUserModel fbUser)
{
IdentityResult result = await _repo.RegisterUser(newUser);
var identityUser = await _repo.FindUser(newUser.UserName, newUser.Password);
fbUser.identityUserID = identityUser.Id;
db.FacebookUserObjects.Add(fbUser);
db.SaveChangesAsync();
}
AngularJS calls to my server:
var _login = function (fbID, fbToken) {
$http.post(serviceBase + 'auth/login', { "fbID": fbID, "access_token": fbToken }).then(function (response) {
var data = "grant_type=password&username=" + fbID + "&password=" + pass;
$http.post(serviceBase + 'auth/token', data, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } })
.success(function (tokenResponse) {
authServiceFactory.bearerToken = tokenResponse.access_token;
})
.error(function (err) {
console.log("token error:", err);
});
authServiceFactory.userObject = response.data;
window.localStorage['userObject'] = JSON.stringify(authServiceFactory.userObject);
})
};
Why would I get the No 'Access-Control-Allow-Origin' error only on the first call, but not subsequent ones?
Update
I have a workaround in place that works, but I don't really like. The issue only arose when calling a second method from my login controller, so if I moved that code up into the login controller instead of a secondary method it works without the CORS error. This really bothers me though and is inefficient, I'd love to know a better way around it.
if you're working with angularjs you might want to check out satellizer. It makes the auth process really simple and has some awesome built in window popup control.
As far as the Access-Control-Allow-Origin calls it could be happening because you explicitly set headers on the one call and the other ones are falling back to the default http provider? Check out $http and see if providing those defaults might work around it.

Facebook login stuck in permissions page in dispaly=touch mode- windows 8

I was trying to implement Facebook login in windows 8 application . I am using following url to request Facebook login
https://www.facebook.com/dialog/oauth?client_id=APP_ID&response_type=token&scope=email%2coffline_access%2cpublish_stream&redirect_uri=http%3a%2f%2fwww.facebook.com%2fconnect%2flogin_success.html&display=touch
I am using webview to request this url .I am getting the login page correctly shown below.
After user enter login credential it is getting redirecting to another page and getting stuck there.I guess that page is supposed to be given permissions.I am attaching a screen shown below
Nothing will happen if i click on cancel or install ..
if i am removing display=touch from the request everything will works fine.But the login poge and permissions page are displayed like in web browser.That is not optimized for touch ..
I have tested the same with webbrowser control in wpf..But the problem is still exisiting there. Any ideas?
On Windows 8 You should be using WebAuthenticationBroker.
Here is code example:
private async void Authenticate()
{
//Facebook Authentication Uri
var facebookUri = "https://www.facebook.com/dialog/oauth";
//Standard redirect uri for desktop/non-web based apps
var redirectUri = "https://www.facebook.com/connect/login_success.html";
//Place your appa client id here
var clientId = "";
//The type of token that can be requested
var responseType = "token";
//Response pattern
var pattern = string.Format("{0}#access_token={1}&expires_in={2}", redirectUri, "(?.+)", "(?.+)");
try
{
String FacebookURL = "https://www.facebook.com/dialog/oauth?client_id=" +
clientId + "&redirect_uri=" + Uri.EscapeUriString(redirectUri) + "&scope=read_stream&display=touch&response_type=token";
System.Uri StartUri = new Uri(FacebookURL);
System.Uri EndUri = new Uri(redirectUri);
WebAuthenticationResult WebAuthenticationResult = await WebAuthenticationBroker.AuthenticateAsync(
WebAuthenticationOptions.None,
StartUri,
EndUri);
if (WebAuthenticationResult.ResponseStatus == WebAuthenticationStatus.Success)
{
var response = WebAuthenticationResult.ResponseData.ToString();
}
else if (WebAuthenticationResult.ResponseStatus == WebAuthenticationStatus.ErrorHttp)
{
//Handle HTTP error
}
else
{
//Handle error
}
}
catch (Exception ex)
{
//Handle error
}
}