Getting issue when I tried with integrating Microsoft Graph SDK with asp.net 4.8 web application - graph

I am facing an issue with Microsoft GRAPH sdk.
IDX21323: RequireNonce is 'True'. OpenIdConnectProtocolValidationContext.Nonce was null, OpenIdConnectProtocol.ValidatedIdToken.Payload.Nonce was not null. The nonce cannot be validated. If you don't need to check the nonce, set OpenIdConnectProtocolValidator.RequireNonce to 'false'. Note if a 'nonce' is found it will be evaluated.
public void ConfigureAuth(IAppBuilder app)
{ app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = appId,
Authority = "https://login.microsoftonline.com/common/v2.0",
Scope = $"openid email profile offline_access {graphScopes}",
RedirectUri = redirectUri,
PostLogoutRedirectUri = redirectUri,
TokenValidationParameters = new TokenValidationParameters
{
// For demo purposes only, see below
ValidateIssuer = false
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailedAsync,
AuthorizationCodeReceived = OnAuthorizationCodeReceivedAsync
}
}
);
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
}
private static Task OnAuthenticationFailedAsync(AuthenticationFailedNotification<OpenIdConnectMessage,
OpenIdConnectAuthenticationOptions> notification)
{
notification.HandleResponse();
string redirect = $"/ErrPage/Errormsg?message={notification.Exception.Message}";
if (notification.ProtocolMessage != null && !string.IsNullOrEmpty(notification.ProtocolMessage.ErrorDescription))
{
redirect += $"&debug={notification.ProtocolMessage.ErrorDescription}";
}
notification.Response.Redirect(redirect);
return Task.FromResult(0);
}

Related

Redirect users to login page after session timeout using OpenIdConnectAuthentication

I'm using Azure B2C in an ASP.NET application with OpenIdConnectAuthentication. My sign-in policy has an absolute session length of 90 minutes.
After the session expires, I'd like the user to be automatically redirected to the login page, so they know they've been logged out. This doesn't seem to happen automatically. I was hoping this could be done automatically, perhaps using OWIN.
For now, as a workaround, I set a Refresh header in all HTTP responses that uses the remaining session life (calculated based on the iat claim and current time). But I was hoping this could just be handled automatically from the server. Is there any way to make the redirect occur automatically from the server-side once the session expires?
Below is my Startup class in Startup.Auth.cs:
public void ConfigureAuth(IAppBuilder app)
{
// ... other code ... //
app.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(SignInPolicyId));
}
private OpenIdConnectAuthenticationOptions CreateOptionsFromPolicy(string policy) {
var options = new OpenIdConnectAuthenticationOptions {
// For each policy, give OWIN the policy-specific metadata address, and
// set the authentication type to the id of the policy
MetadataAddress = String.Format(aadInstance, tenant, policy),
AuthenticationType = policy,
UseTokenLifetime = true,
// These are standard OpenID Connect parameters, with values pulled from
// Web.config
ClientId = clientId,
RedirectUri = redirectUri,
PostLogoutRedirectUri = logoutUri,
Notifications =
new OpenIdConnectAuthenticationNotifications {
AuthenticationFailed = AuthenticationFailed,
RedirectToIdentityProvider =
(context) => {
var value = context.OwinContext.Request.Path.Value;
if (value != "/default.aspx") {
context.OwinContext.Response.Redirect("/");
context.HandleResponse();
}
return Task.FromResult(0);
}
},
Scope = "openid",
ResponseType = "id_token",
TokenValidationParameters =
new TokenValidationParameters {
NameClaimType = "name",
},
};
return options;
}

Is it possible to Implement Azure Ad Authentication in webforms without cookie authentication type?

the below is working code.
But I need to implement the solution without sing coockies (UseCookieAuthentication).
After Azure authentication, when we check the AuthenticationTyoe in Identity it shown as Cookies, which i need to avoid. any suggetions/other implementations
public void Configuration(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
// Sets the ClientId, authority, RedirectUri as obtained from web.config
ClientId = clientId,
Authority = authority,
RedirectUri = redirectUri,
// PostLogoutRedirectUri is the page that users will be redirected to after sign-out. In this case, it is using the home page
PostLogoutRedirectUri = redirectUri,
Scope = OpenIdConnectScope.OpenIdProfile,
// ResponseType is set to request the code id_token - which contains basic information about the signed-in user
ResponseType = OpenIdConnectResponseType.CodeIdToken,
// OpenIdConnectAuthenticationNotifications configures OWIN to send notification of failed authentications to OnAuthenticationFailed method
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailed
}
}
);
}

User unauthorized after Azure AD login to different application simultaneously

I have two MVC applications AppA and AppB, and implemented Azure AD authentication for login.
I am able to sign-in successfully to both applications.
But the issue is, after I login to AppA and then to AppB, after sometime when I return back to AppA I am facing the issue where user has been logged out, and it again redirects to login screen (in AppA).
After I login to AppA (second time) and go back to AppB (user in AppB is logged out).
Client IDs are different ; TenandID is same. Both apps are hosted in same server.
Startup file:
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
SlidingExpiration = true,
Provider = new CookieAuthenticationProvider
{
OnResponseSignIn = context =>
{
context.Properties.AllowRefresh = true;
context.Properties.ExpiresUtc = DateTimeOffset.UtcNow.AddDays(1);
},
OnValidateIdentity = MyCookieValidateIdentity
},
ExpireTimeSpan = TimeSpan.FromDays(2)
});
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = appId,
//CookieManager=new SameSiteCookieManager(new SystemWebCookieManager()),
Authority = "https://login.microsoftonline.com/xxxxxx/v2.0",
Scope = $"openid email profile offline_access {graphScopes}",
RedirectUri = redirectUri,
PostLogoutRedirectUri = redirectUri,
TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
RedirectToIdentityProvider = (context) =>
{
context.ProtocolMessage.DomainHint = "xyz.com";
return Task.FromResult(0);
},
// SecurityTokenValidated = OnSecurityTokenValidated,
AuthenticationFailed = OnAuthenticationFailedAsync,
AuthorizationCodeReceived = OnAuthorizationCodeReceivedAsync
}
}
);
}
actionContext.RequestContext.Principal.Identity.IsAuthenticated is returning False
I am assuming it has to do something with the cookie. Can someone please help resolve this ?
Edit:
Debugged further and found:
Initially if the cookies for AppA are set as:
.AspNet.Cookies = A_abc123 ; ASP.NET_SessionId = A_def456
And for AppB .AspNet.Cookies = B_mno123 ; ASP.NET_SessionId = B_pqr456
Then after I click any link in AppA, the cookie's values are updated with AppB's cookies, i.e. .AspNet.Cookies = B_mno123 ; ASP.NET_SessionId = B_pqr456
.AspNet.Cookies ASP.NET_SessionId
AppA A_abc123 A_def456
AppB B_mno123 B_pqr456
AppA B_mno123 B_pqr456
One thing that you need to do is to configure the Data Protection API so that both services uses the same cookie protection key. Out of the box each service creates its own unique key, and a cookie from one service is not valid in a different service.
I also did a blog post about the data protection API here.
See
How to: Use Data Protection
Get started with the Data Protection APIs in ASP.NET Core
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
//AuthenticationType = CookieAuthenticationDefaults.AuthenticationType,// DefaultAuthenticationTypes.ApplicationCookie,
CookieName = ".AspNet.AppA.Cookies",
SlidingExpiration = true,
CookieManager = new SystemWebCookieManager(),
Provider = new CookieAuthenticationProvider
{
OnResponseSignIn = context =>
{
context.Properties.AllowRefresh = true;
context.Properties.ExpiresUtc = DateTimeOffset.UtcNow.AddDays(1);
},
},
ExpireTimeSpan = TimeSpan.FromDays(2)
});
//... code removed for brevity //
}
The Default Cookie Name set by the application was: .AspNet.Cookies
And when I modified the default cookie name, the issue got resolved. Each application was generating its own cookiename and hence the other application was not signing out the user.

MSAL for OpenIdConnect?

we have 3 web apps which use OpenIdConnect with the OWIN app builder for user sign in. The packages used in the Startup are:
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.OpenIdConnect;
This is the Configuration method, some code skipped for brevity:
public void Configuration(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = commonAuthority,
PostLogoutRedirectUri = postLogoutRedirectUri,
RedirectUri = postLogoutRedirectUri,
TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
ValidateIssuer = false,
RoleClaimType = "roles",
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = AuthorizationCodeReceived,
AuthenticationFailed = context =>
{
context.HandleResponse();
context.Response.Redirect("/Error/ShowError?signIn=true&errorMessage=" + context.Exception.Message);
return Task.FromResult(0);
}
}
});
}
I don't see any mention of Microsoft.IdentityModel.Clients.ActiveDirectory (ADAL) anywhere in the Startup code. Correct me if I am wrong but I think there is no action to be taken as part of ADAL to MSAL for OpenIdConnect authentication components.

Microsoft Graph in asp.net web forms access token expires - how to refresh tokens in web forms application and not MVC

I have an asp.net 4.6 web forms application (no MVC). I am updating the security in my application. I am using OpenIdConnectAuthentication to authenticate with our Azure AD. Then I pass the access token to Microsoft graph to send an email with Office 365. My token is set to expire in 60 minutes. I either need to expand the expiration to 8 hours or refresh the token. Without having MVC I am not sure how to handle this. I am looking for help with direction to take and possibly code samples.
(I original tried to utilize an MVC sample and put it into my project using a Session Token class. Once we tested with multiple users I believe I had a memory leak and it would crash in about 5 minutes.)
Startup code:
public class Startup
{
private readonly string _clientId = ConfigurationManager.AppSettings["ClientId"];
private readonly string _redirectUri = ConfigurationManager.AppSettings["RedirectUri"];
private readonly string _authority = ConfigurationManager.AppSettings["Authority"];
private readonly string _clientSecret = ConfigurationManager.AppSettings["ClientSecret"];
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
CookieManager = new SystemWebCookieManager(),
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = _clientId,
ClientSecret = _clientSecret,
//Authority = _authority,
Authority = String.Format(_authority, domain, "/v2.0"),
RedirectUri = _redirectUri,
ResponseType = OpenIdConnectResponseType.CodeIdToken,
Scope = OpenIdConnectScope.OpenIdProfile,
UseTokenLifetime = false,
TokenValidationParameters = new TokenValidationParameters { NameClaimType = "name", RequireExpirationTime = false},
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = async n =>
{
// Exchange code for access and ID tokens
var auth = String.Format(_authority, "common/oauth2/v2.0", "/token");
var tokenClient = new TokenClient($"{auth}", _clientId, _clientSecret);
var tokenResponse = await tokenClient.RequestAuthorizationCodeAsync(n.Code, _redirectUri);
if (tokenResponse.IsError)
{
throw new Exception(tokenResponse.Error);
}
var claims = new List<Claim>()
{
new Claim("id_token", tokenResponse.IdentityToken),
new Claim("access_token", tokenResponse.AccessToken)
};
n.AuthenticationTicket.Identity.AddClaims(claims);
},
},
});
}
}
SDK Helper:
public class SDKHelper
{
// Get an authenticated Microsoft Graph Service client.
public static GraphServiceClient GetAuthenticatedClient()
{
GraphServiceClient graphClient = new GraphServiceClient(
new DelegateAuthenticationProvider(
async (requestMessage) =>
{
string accessToken = System.Security.Claims.ClaimsPrincipal.Current.FindFirst("access_token").Value;
// Append the access token to the request.
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", accessToken);
// Get event times in the current time zone.
requestMessage.Headers.Add("Prefer", "outlook.timezone=\"" + TimeZoneInfo.Local.Id + "\"");
// This header has been added to identify our sample in the Microsoft Graph service. If extracting this code for your project please remove.
requestMessage.Headers.Add("SampleID", "aspnet-snippets-sample");
}));
return graphClient;
}
}
Sending Email:
GraphServiceClient graphClient = SDKHelper.GetAuthenticatedClient();
string address = emailaddress;
string guid = Guid.NewGuid().ToString();
List<Recipient> recipients = new List<Recipient>();
recipients.Add(new Recipient
{
EmailAddress = new Microsoft.Graph.EmailAddress
{
Address = address
}
});
// Create the message.
Message email = new Message
{
Body = new ItemBody
{
ContentType = Microsoft.Graph.BodyType.Text,
},
Subject = "TEST",
ToRecipients = recipients,
From = new Recipient
{
EmailAddress = new Microsoft.Graph.EmailAddress
{
Address = address
}
}
};
// Send the message.
try
{
graphClient.Me.SendMail(email, true).Request().PostAsync().Wait();
}
catch (ServiceException exMsg)
{
}
You need to request the scope offline_access. Once you've requested that, the /token endpoint will return both an access_token and a refresh_token. When your token expires, you can make another call to the /token endpoint to request a new set of access and refresh tokens.
You might find this article helpful: Microsoft v2 Endpoint Primer. In particular, the section on refresh tokens.

Resources