The VS 2013 SPA Template is configured with the cookie middleware, among other middlewares like the OAuth MW or ExternalCookie MW.
// 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"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(20),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
// Use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
Since the WebApi will authorize the calls made from the client side through bearer tokens and the external cookie middleware is there to support external login providers, what role does the cookie middleware serve in this?
It's there as a replacement for forms authentication. Before, a user would sign in with forms auth, then it would issue a cookie, and create a principal object that represents the user's identity. With OWIN, the cookie authentication middleware does the same task.
It will also handle things like logins/redirects (forbidden requests) as you can see with the above line of code:
LoginPath = new PathString("/Account/Login"),
Related
I'm currently using the 1.5 version of Roundcube and trying to configure it to work with the Yahoo mail server. I have created an app and have also allowed the email and profile API permissions from the OpenID.
There is an issue with the scopes and when trying to log in it redirects back to an error page and says: "Please request scope from registered scopes and submit again".
This is the current config for Roundcube:
// Enable OAuth2 by defining a provider. Use 'generic' here
$config['oauth_provider'] = 'yahoo';
// Provider name to be displayed on the login button
$config['oauth_provider_name'] = 'Yahoo';
// Mandatory: OAuth client ID for your Roundcube installation
$config['oauth_client_id'] = '------';
// Mandatory: OAuth client secret
$config['oauth_client_secret'] = '------';
// Mandatory: URI for OAuth user authentication (redirect)
$config['oauth_auth_uri'] = 'https://api.login.yahoo.com/oauth2/request_auth';
// Mandatory: Endpoint for OAuth authentication requests (server-to-server)
$config['oauth_token_uri'] = 'https://api.login.yahoo.com/oauth2/get_token';
// Optional: Endpoint to query user identity if not provided in auth response
//$config['oauth_identity_uri'] = 'null';
// Optional: disable SSL certificate check on HTTP requests to OAuth server
// See http://docs.guzzlephp.org/en/stable/request-options.html#verify for possible values
$config['oauth_verify_peer'] = true;
// Mandatory: OAuth scopes to request (space-separated string)
$config['oauth_scope'] = 'openid mail-r mail-w sdct-w';
// Optional: additional query parameters to send with login request (hash array)
$config['oauth_auth_parameters'] = ['nonce' => mt_rand(), 'prompt' => 'consent'];
// Optional: array of field names used to resolve the username within the identity information
$config['oauth_identity_fields'] = null;
// Boolean: automatically redirect to OAuth login when opening Roundcube without a valid session
$config['oauth_login_redirect'] = true;
Here are the settings for the app:
https://imgur.com/pwtNsvx
The error:
https://imgur.com/i11vMnM
https://imgur.com/Ar0tvnK
https://imgur.com/5Lertax
https://imgur.com/aRLVOLt
https://imgur.com/7d2L4Xv
You need to request access to restricted scopes first. Once and if approved you need to create an app and select the mail scope (the email scope is to get access to the users "email" identity, not for access to the mailbox). See https://developer.verizonmedia.com/mail
I am developing a C# Web Api (.NET Framework) and would like to use in parallel the AAD authentication (already working correctly) and Google Authentication. This means, my clients (javascript or just Postman) should fetch the token, include it in the Authorization header (Bearer token) and be able to execute the API methods.
However, it seems that the token I am generating with Postman is not accepted by my C# Web Api. I am always getting a HTTP 401. The AAD (Windows Azure Active Directory) works seamlessly.
Using a C# Web Api (.Net Framework 4.6.1), including Microsoft.Owin.Security.Google Nuget package v.4.0.1.
I have created the Oauth client on the google developer console, got my client ID and client secret. Also I have set up the redirect URI there.
I am using Postman Oauth2 authorization, setting following parameters:
Grant type: implicit
Callback URL: my url
AUth URL: https://accounts.google.com/o/oauth2/v2/auth
Client ID: 65xxxxxmyclientid.apps.googleusercontent.com
scope: openid email profile
Then I can log in with my google account, give consent to use the scopes, and am getting the token like this:
Access Token
ya29.Gls7....
This token is then sent as Authorization header like "Bearer ya29.Gls7...."
Startup.Auth
public void ConfigureAuth(IAppBuilder app)
{
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
TokenValidationParameters = new TokenValidationParameters
{
ValidAudience = ConfigurationManager.AppSettings["ida:Audience"]
},
});
app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
{
ClientId = ConfigurationManager.AppSettings["GoogleClientId"],
ClientSecret = ConfigurationManager.AppSettings["GoogleClientSecret"],
});
}
ValuesController
public class ValuesController : ApiController
{
[Authorize]
// GET api/values/5
public string Get(int id)
{
return "value";
}
}
Every time I call the API using Postman, I am getting a 401, even though the bearer token is included in the request authorization header.
HTTP GET https://localhost:44385/api/values/1
Edit: There is no custom code from my side which would validate the token. I assumed that is being done automatically by the library. In fact, this is done automatically for AAD tokens, but not in the case of Google Oauth.
I expect to get inside the code of the controller method, having at least the identity name set with the google account name/email.
What am I missing to be authenticated inside my controller method with google account?
When User Sign In Gmail account via Oauth2 protocol and finish it, my server get authorization code and I make exchange this code for refresh token and access token, everything works as planned but I need to get email address too. I mean if user logged in as helloworld#gmail.com, somehow with authorization code I would like to know this address, may I somehow to know it?
This is endpoint where I exchange authorization code on access token and refresh token:
public OAuth2AccessToken oauth(String authorizationCode) {
AuthorizationCodeResourceDetails resource = new AuthorizationCodeResourceDetails();
resource.setUserAuthorizationUri(userAuthorizationUri);
resource.setAccessTokenUri(accessTokenUri);
resource.setClientId(clientId);
resource.setClientSecret(clientSecret);
resource.setPreEstablishedRedirectUri(redirectUrl);
resource.setScope(scopes);
resource.setUseCurrentUri(false);
AccessTokenRequest request = new DefaultAccessTokenRequest();
request.setPreservedState(new Object());
request.setAuthorizationCode(authorizationCode);
AuthorizationCodeAccessTokenProvider provider = new AuthorizationCodeAccessTokenProvider();
OAuth2AccessToken accessToken = provider.obtainAccessToken(resource, request);
return accessToken;
}
I don't have WebSecurityConfigurerAdapter for OAuth2
If the user's email address is not already provided in the id_token part of the oauth2 response, you can use the Gmail API Users.getProfile operation, using the special value "me" as the userId to refer to the authenticated user.
See: https://developers.google.com/gmail/api/v1/reference/users/getProfile
That should give you a response like:
{
"emailAddress": -string-,
"messagesTotal": -integer-,
"threadsTotal": -integer-,
"historyId": -unsigned long-
}
I have a SPA application that uses MSAL to acquire a token from AAD. Because MSAL works with v2 endpoint and because v2 endpoint does not currently support issuing tokens for custom API's, I'm passing the ID Token to my api and essentially treating my api as the same application. (While this has a smell to it, it does actually work -- at least with Nodejs API).
SPA app
let idToken = Msal.Storage('localStorage').getItem(Msal.Constants.idTokenKey);
this.http.configure(config => {
config.withBaseUrl("http://localhost:3001/")
config.withDefaults({headers: {'Authorization': 'Bearer ' + idToken}})
});
//Call API
this.http.fetch("account")
...
Node.js API
//Using express/passport
var BearerStrategy = require("passport-azure-ad").BearerStrategy;
var options = {
identityMetadata: "https://login.microsoftonline.com/tenantid/.well-known/openid-configuration/",
clientID: "xxxxxxx-xxxx-xxxxxxx-xxxxx",
passReqtoCallback: false,
validateIssuer: true,
issuer: "http://login.microsoftonline.com/{tenantid}/v2.0"
};
app.get("/account",passport.authenticate('oauth-bearer',{session: false}),...
The above all works. Once a user authenticates with the SPA, the token is passed and the call to the Node API works.
I'm now trying to replace the Nodejs API with a .Net WebAPI. I have the following:
Startup.cs
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
TokenValidationParameters = new TokenValidationParameters
{
//Same ID as used for ClientID in Nodejs
ValidAudience = "xxxxxx-xxxxx-xxxxx-xxxxx",
ValidIssuer = "https://login.microsoftonline.com/{tenantid}/v2.0",
ValidateIssuer = true,
AuthenticationType = "WebApi" //Tried both with and without this
},
Tenant = "{tenantid}" //have tried both id and name
}
)
AccountController.cs
[Authorize]
[Route("account")]
public IHttpActionResult AccountProfile(){
//Get Account information
....
return Ok(profile);
}
However, when I point the SPA app to call the .Net api, I always get Authorization has been denied for this request .
Is there something I'm missing?
Edit
Incidentally, I've inspected the token that is being used.
The value I'm using for clientID (Nodejs) and ValidAudience (.Net) exactly match the aud claim in the token. The issuer (Nodejs) and ValidIssuer (.Net) exactly match the iss claim in the token. Lastly, the anywhere in my code where I've inserted {tenantid}, the actual value there matches exactly the tid claim in the token.
We had a similar issue when switching from ADAL to MSAL and got it to work by using a similar approach like this Github project. Specifically take a look at these files:
https://github.com/oktadeveloper/okta-oauth-aspnet-codeflow/blob/master/Api/Startup.cs
https://github.com/oktadeveloper/okta-oauth-aspnet-codeflow/blob/master/Api/OpenIdConnectCachingSecurityTokenProvider.cs
Update: Our Startup.cs:
var provider = new OpenIdConnectCachingSecurityTokenProvider(
string.Format(bc2Instace, tenant, policyId));
var jwt = new JwtFormat(clientId, provider);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
{
AccessTokenFormat = jwt,
});
Given:
A JavaScript app that authenticates with oidc over identityerver v3
A Asp.net Core Webapi that authenticates with the given bearer token to identiyserver
The Javascript clients makes calls with the access token itself to the api.
Problem:
The Authentication suceeds but the restored principal is missing some custom claim like "username" and "familyName". I can see that the oidc client in the javascript client has these informations
some claims like "idp" is set in both Javascript and Api Client. But bot are not handled explicitly.
The main difference is that idp is part of the access_token which the username is not.
the configuration of the api is :
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
var authority = config["identity:authority:url"];
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
LegacyAudienceValidation = true,
Authority = authority,
RequireHttpsMetadata = false,
EnableCaching = false,
ApiName = "MyApp.Read",
});
Any hint what i'm missing ? ( I assume it is some kind of profile read in the api?)
Workaround
I extend the configuration with JwtBearerEvents and make a manual read with userclient when the token was authenticated like this
JwtBearerEvents = new JwtBearerEvents
{
OnTokenValidated = async context =>
{
string header = context.Request.Headers["Authorization"];
string accessToken = header.Substring(6);
var result = await userInfoClient.GetAsync(accessToken);
but is this the intended way? Are extended / profile claims meant to be returned only by manually querying them?