I've successfully setup a custom authentication process with Azure Mobile Services and my Windows Phone 8.1 app (following the guide here)
I'm now creating an MVC5 single page application (SPA) to manage the admin side of things for the system. I'm relatively new to MVC5 and just need a little help to get started with performing a login just like in my phone app?
Currently my phone app performs a login by
App.MobileService.CurrentUser = await AuthenticateAsync(this.textBox_email.Text, textBox_password.Password);
which does
private async Task<MobileServiceUser> AuthenticateAsync(string username, string password)
{
// Call the CustomLogin API and set the returned MobileServiceUser
// as the current user.
var user = await App.MobileService
.InvokeApiAsync<LoginRequest, MobileServiceUser>(
"CustomLogin", new LoginRequest()
{
UserName = username,
Password = password
});
return user;
}
this all works well so I guess the question is how do I do make a call to my customer authentication API in the same way in MVC5 and set the user context if successful?
Startup.Auth.cs:
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
// Enable the application to use a cookie to store information for the signed in user
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login")
});
// Use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Uncomment the following lines to enable logging in with third party login providers
//app.UseMicrosoftAccountAuthentication(
// clientId: "",
// clientSecret: "");
//app.UseTwitterAuthentication(
// consumerKey: "",
// consumerSecret: "");
//app.UseFacebookAuthentication(
// appId: "",
// appSecret: "");
//app.UseGoogleAuthentication();
}
Let me know if I'm missing any info or detail.
Thanks!
Unfortunately this is not easy to do in Mobile Services. While you could achieve login using the Mobile Services HTML/JS SDK (served in an MVC view), this will not set the user context.
Because of Mobile Services incompatibility with MVC (addressed in the new Mobile Apps product), you won't be able to rely on that SDK. Unfortunately that means writing custom middleware/filters.
The easiest solution is probably to package your username/password validation and storage logic into code that can be shared by your Mobile Services project and your MVC project. The MVC project would need to take the validated user and issue a session cookie which is then read by a custom middleware or filter.
Writing an AuthorizationFilter implementation will be much easier than an OWIN middleware, so I would recommend that approach. Check if the cookie is present and valid, and if so set the user context.
Related
Google auth window does not open for authorization on IIS.
I'm publishing my native app and moving it to the server. However, it does not work on the server (IIS). I request your help on the matter.
https://aycokucuz.com/
https://github.com/NortOfKing/TestYok
https://youtu.be/uMAEg4lHc88
The issue is with your code. From the YouTube video i can see that you are using GoogleWebAuthorizationBroker. This method of authorization is designed for installed applications. It will open the web browser window on the machine that it is running on in this case your web server.
var credential = GoogleWebAuthorizationBroker.AuthorizeAsync(GoogleClientSecrets.Load(stream).Secrets,
scopes,
userName,
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
What you want to do is change your code to use the web flow so that your going to be opening the consent screen on the users client.
public void ConfigureServices(IServiceCollection services)
{
...
// This configures Google.Apis.Auth.AspNetCore3 for use in this app.
services
.AddAuthentication(o =>
{
// This forces challenge results to be handled by Google OpenID Handler, so there's no
// need to add an AccountController that emits challenges for Login.
o.DefaultChallengeScheme = GoogleOpenIdConnectDefaults.AuthenticationScheme;
// This forces forbid results to be handled by Google OpenID Handler, which checks if
// extra scopes are required and does automatic incremental auth.
o.DefaultForbidScheme = GoogleOpenIdConnectDefaults.AuthenticationScheme;
// Default scheme that will handle everything else.
// Once a user is authenticated, the OAuth2 token info is stored in cookies.
o.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie()
.AddGoogleOpenIdConnect(options =>
{
options.ClientId = {YOUR_CLIENT_ID};
options.ClientSecret = {YOUR_CLIENT_SECRET};
});
}
See full samples here. #web-applications-asp.net-core-3
I have an ASP.NET MVC 5 application that uses ASP.NET Identity 2/OWIN that has it's own login using the following:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString(AppConfiguration.LoginPath),
Provider = new CookieAuthenticationProvider
{
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser, int>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentityCallback: (manager, user) => user.GenerateUserIdentityAsync(manager),
getUserIdCallback: (id) => (Int32.Parse(id.GetUserId())))
}
});
In Addition to our own authentication set up above in the startup, we'd like to also introduce authentication using an external app that uses Identity Server 4 (basically just so a user in that app can sso into ours), using open id connect, setting that up after the above code like:
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
Authority = AppConfiguration.ExternalServerAuthority,
ClientId = "xxxxxxxxxx",
ClientSecret = "secret",
RedirectUri = "http://localhost:1045/signin-oidc",
ResponseType = "id_token",
RequireHttpsMetadata = false,
PostLogoutRedirectUri = "http://localhost:1045/signout-callback-oidc",
SignInAsAuthenticationType = "Cookies",
Notifications = new OpenIdConnectAuthenticationNotifications
{
SecurityTokenValidated = async context =>
{
var appAuthManager = DependencyResolver.Current.GetService<IApplicationAuthenticationManager>();
var userManager = DependencyResolver.Current.GetService<IApplicationUserManager>();
var email = context.AuthenticationTicket.Identity.FindFirst("preferred_username");
var user = userManager.FindByName(email.Value, null);
if (user == null)
{
return;
}
await appAuthManager.SignInAsync(user, false, false);
}
}
});
We need to be able to do a SignInAsync when we get the token from the external app because we need the user to be signed in as the actual user in our own app. Part of our problem is also, if we set this up, then whenever a user is not logged in, they are always sent to the other external app to log in if trying to access a resource/page that they must be logged in for - it no longer sends them to our existing login page (which would give them the option of logging in there like they normally would or clicking a link to take them to the other app to log in if they have a user account for that app too). We don't want this because not all our users will be using/have access to this other app, it's really only for some users, mostly to conveniently navigate into our app from the other app without having to separately sign in to ours. So we can't have all unauthorized requests sent to this other app to log in. How can we achieve that? Is there a better way to set that up here?
Edit for more info:
To explain more clearly the pieces here and what needs to happen. There are actually three applications at play. There is our app, an MVC 5 app that has it's own login page, uses owin/asp.net identity for user authentication and to store/manage its users. There is now another app (for another company that wants to work with us), which is a SPA app that authenticates against a separate IdentityServer4 server run by the same company. This SPA app, wants to put a link in it that sends a user to our MVC 5 app without the user having to actually log in to our MVC 5 app - so, they want to SSO into our MVC 5 app (by use setting up our app to use oidc connect to authenticate the user against the SPA's identity server). So when they get to our app, we need to actually log them in as our user....but we have to also make sure that all our users are not sent to this external app to log in when they are not currently logged in to our app because not all our users will have access to this external app. I hope that clears this up.
I'm new to owin, OAuth, OpenID connect terminology. I'm trying learn as much as possible. I work with legacy .net web forms application. I have 2 webforms app. App_1 is more like a dashboard/landing page application. App_2 is the main application that has all the functionalities. I use Identity server 4 for authentication only(don't ask me why).
this is the flow:
1) user logs in using IS4 and lands in App_1.
2) all the metadata required for the authorization and business logic is stored in the session object in app_1 page load
3) the user then goes to app_2. it retrieves required data from the session object
previously there was no IS4 authentication. it was using forms authentication and the session concept was working fine. I just changed authentication to IS4 then the session object became null.
I tried various solutions suggested on the internet. It didn't work. I added this in app_1 startup.cs, but still it failed.
app.Use((context, next) =>
{
var httpContext = context.Get<HttpContextBase>(typeof(HttpContextBase).FullName);
httpContext.SetSessionStateBehavior(SessionStateBehavior.Required);
return next();
});
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationType = "Cookies",
ExpireTimeSpan = TimeSpan.FromMinutes(10),
CookieName="MyWebApp",
SlidingExpiration = true
});
Note: im also having the same machine key to in web.config of both the web apps.
Can anyone please tell me how to carry the session object between these 2 web apps?
We have a SharePoint publishing site with anonymous access hosted on the internet. As per out latest requirements, we need to implement user login (AzureAD, Microsoft personal and work accounts, and more).
https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-flows
As per the documentation here, we want to implement this using Web API to get the secure information from the database. We are thinking about using MSAL.js file for user login and logout on the SharePoint and after getting a bearer token we can call the Web API for the additional data from our database.
Standalone Web APIs restriction: “You can use the v2.0 endpoint to build a Web API that is secured with OAuth 2.0. However, that Web API can receive tokens only from an application that has the same Application ID. You cannot access a Web API from a client that has a different Application ID. The client won't be able to request or obtain permissions to your Web API.”
How can we create two applications with same application ID at App Registration Portal? Or should we use the same application ID at SharePoint and Web API’s end?
There is no need to register two application, you only need to one register application. After you register the application, you can using the MSAL library below to get the token to call the web API:
<script class="pre">
var userAgentApplication = new Msal.UserAgentApplication("e5e5f2d3-4f6a-461d-b515-efd11d50c338", null, function (errorDes, token, error, tokenType) {
// this callback is called after loginRedirect OR acquireTokenRedirect (not used for loginPopup/aquireTokenPopup)
})
userAgentApplication.loginPopup(["user.read"]).then(function (token) {
var user = userAgentApplication.getUser();
console.log(token);
// signin successful
}, function (error) {
// handle error
});
</script>
And to protect the web API, you can use the same app and refer the code below:
public void ConfigureAuth(IAppBuilder app)
{
var tvps = new TokenValidationParameters
{
// The web app and the service are sharing the same clientId
ValidAudience = "e5e5f2d3-4f6a-461d-b515-efd11d50c338",
ValidateIssuer = false,
};
// NOTE: The usual WindowsAzureActiveDirectoryBearerAuthenticaitonMiddleware uses a
// metadata endpoint which is not supported by the v2.0 endpoint. Instead, this
// OpenIdConenctCachingSecurityTokenProvider can be used to fetch & use the OpenIdConnect
// metadata document.
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
{
AccessTokenFormat = new JwtFormat(tvps, new OpenIdConnectCachingSecurityTokenProvider("https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration")),
});
}
The current application that we are developing consists of 2 applications. A WebApi application, and a MVC frontend application. For the WebApi i added support for bearer token authorization via OWIN. These applications run as seperate websites within the same domain but with their own subdomains site.xxx.xxx, api.xxx.xxx
Authenticating to the WebAPi, f.e. with Postman, works as designed, the principal and identity objects, including the claims, are initialized properly.
The question arises when i want to login to the WEbApi from within the Mvc application.
Is there any way to get the ClaimsPrincipal and the ClaimsIdentity in our MVC application after logging in via the WebAPI via the /token url somewhat sharing the OWIN context, or should we implement the same OWIN authorization functionality inside the MVC application to create a seperate autorization "route"?
Yes, there is. Couple things to note
The token you get back from the web api will be encrypted by default. Your web application needs to decrypt this token to be able to extract the claims from the bearer token. For this, you have to have the same machine key on both of the servers (your webapi web.config and mvc web.config needs to have the same machine key)
Your MVC web app needs to wire up both bearer tokens and application cookies. Your startup.auth.cs might include something like this:
public static OAuthBearerAuthenticationOptions OAuthBearerOptions { get; private set; }
static Startup()
{
OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
}
public void ConfigureAuth(IAppBuilder app)
{
app.UseOAuthBearerAuthentication(OAuthBearerOptions);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login")
});
}
Now in your login method
//Assume that the token that you got from web api is in the variable called accessToken
//Decrypt this token first. If your machine keys are the same, the following line will work
var unencryptedToken = Startup.OAuthBearerOptions.AccessTokenFormat.Unprotect(accessToken);
//Next, extract the claims identity from the token
var identity = unencryptedToken.Identity;
//Need to create a claims identity that uses a cookie (not a bearer token). An MVC app
//knows how to deal with a claims identity using an application cookie, but doesn't know
//how to deal with a claims identity using a bearer token. So this is a translation step
//from a web api authentication mechanism to the mvc authentication mechanism
var id = new ClaimsIdentity(identity.Claims, DefaultAuthenticationTypes.ApplicationCookie);
//At this moment, your new claims identity using an application cookie is ready, but we still
//need to sign in. Use the OWIN Auth manager from the context to sign in. This will create
//the application cookie and correctly populate User.IsAuthenticated(). From now on, you are
//logged in
AuthenticationManager.SignIn(id);