Closed vipswelt closed 5 years ago
@vipswelt Please take a look at this Wiki topic on SSO between apps using MSAL.js and let us know if it resolves your issue. https://github.com/AzureAD/microsoft-authentication-library-for-js/wiki/Sso#sso-between-apps
@navyasric not resolved
getting error while already autheticated with with my .net app http://localhost:4200/auth-callback#error=interaction_required&error_description=AADB2C90077%3a+User+does+not+have+an+existing+session+and+request+prompt+parameter+has+a+value+of+%27None%27.%0d%0aCorrelation+ID%3a+d60f7e7e-ed42-40ab-a334-d7ef620db4b2%0d%0aTimestamp%3a+2018-11-22+07%3a37%3a01Z%0d%0a&state=9a1e5b76-f589-41c2-a3af-5f61fcd6acef
No one is replying, no one has a solution? please reply its really urgent for me
@vipswelt Can you provide the exact steps to reproduce, library version , browser details, etc as per the issue template? That will help us help you better. Are both your apps(.net and angular) running in the same browser? Also, as per the wiki doc linked above, did you pass a login_hint of the user and then call acquireTokenSilent?
Hi @navyasric, Yes, both apps running on the same browser: Google Chrome (Version 70.0.3538.110 (Official Build) (64-bit)). But both apps are not able to retrieve the session from another logged in app. I can't use login_hint because I don't have any detail for any user going to be logged in. I am attaching a code snippet with this reply please have a quick look over it
### For Angular App (I am using @azure/msal-angular package in angular 6)
Config:
`{
clientID: '4fe0a851-aec1-4241-8e46-fe13056f8d2e',
authority: 'https://login.microsoftonline.com/tfp/******.onmicrosoft.com/b2c_1_susi/v2.0/.well-known/openid-configuration',
tokenReceivedCallback: true,
validateAuthority: false,
cacheLocation: 'localStorage',
redirectUri: 'http://localhost:4200/auth-callback',
postLogoutRedirectUri: 'http://localhost:54503/',
logger: loggerCallback,
// loadFrameTimeout: number;
navigateToLoginRequestUrl: false,
popUp: false,
consentScopes:
[
'https://******.onmicrosoft.com/finreg-api/read',
'https://******.onmicrosoft.com/finreg-api/write',
'https://******.onmicrosoft.com/finreg-api/profile',
'https://******.onmicrosoft.com/finreg-api/offline_access',
'https://******.onmicrosoft.com/finreg-api/open_id',
'https://******.onmicrosoft.com/finreg-api/graph',
'https://******.onmicrosoft.com/finreg-api/user_impersonation'
],
// isAngular: true,
unprotectedResources: ['https://www.microsoft.com/en-us/'],
protectedResourceMap: protectedResourceMap,
// extraQueryParameters: '';
correlationId: '1234',
level: LogLevel.Info,
piiLoggingEnabled: true
}`
app.module.ts:
`imports: [
BrowserModule,
BrowserAnimationsModule,
FormsModule,
ReactiveFormsModule,
MatAutocompleteModule,
MatInputModule,
RouterModule.forRoot(AppRoutes, { useHash: false }),
HttpClientModule,
MomentModule,
ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }),
DistributedUIModule,
**MsalModule.forRoot(environment.config.msal_config),**
],
providers: [
...MAIN_SERVICES,
**{
provide: HTTP_INTERCEPTORS,
useClass: MsalInterceptor,
multi: true
},**
{ provide: LocationStrategy, useClass: PathLocationStrategy },
{ provide: CookieService, useFactory: cookieServiceFactory }
]`
AuthGaurd.ts:
canActivate(): boolean { if (this._msalService._oauthData.isAuthenticated) { return true; } // tslint:disable-next-line:max-line-length this._msalService.loginRedirect(environment.config.msal_config.consentScopes); // , '&login_hint=weltofvips@gmail.com&domain_hint=organizations'); // this._msalService.loginRedirect(environment.config.msal_config.consentScopes); // this._msalService.acquireTokenSilent([environment.config.msal_config.authority]); // tslint:disable-next-line:max-line-length // this._msalService.loginRedirect(environment.config.msal_config.consentScopes, '&prompt=none&organizations&login_hint=myemail@domain.com'); return false; }
### For .net MVC app:
web.config:
`
<add key="ida:ClientSecret" value="0n50*************.g1C|RS" />
<add key="ida:AadInstance" value="https://login.microsoftonline.com/tfp/{0}/{1}/v2.0/.well-known/openid-configuration" />
<add key="ida:RedirectUri" value="http://localhost:54503" />
<add key="ida:SignUpSignInPolicyId" value="b2c_1_susi" />
<add key="ida:EditProfilePolicyId" value="b2c_1_edit_profile" />
<add key="ida:ResetPasswordPolicyId" value="b2c_1_reset" />
<!-- Uncomment the localhost url if you want to run the API locally -->
<add key="api:TaskServiceUrl" value="https://********.azurewebsites.net/" />
<!-- The following settings is used for requesting access tokens -->
<add key="api:ApiIdentifier" value="https://********.onmicrosoft.com/HubApi/" />
<add key="api:ReadScope" value="read" />
<add key="api:WriteScope" value="write" />`
Startup.Auth.cs:
`public partial class Startup
{
// App config settings
public static string ClientId = ConfigurationManager.AppSettings["ida:ClientId"];
public static string ClientSecret = ConfigurationManager.AppSettings["ida:ClientSecret"];
public static string AadInstance = ConfigurationManager.AppSettings["ida:AadInstance"];
public static string Tenant = ConfigurationManager.AppSettings["ida:Tenant"];
public static string RedirectUri = ConfigurationManager.AppSettings["ida:RedirectUri"];
public static string SignUpRedirectUri = ConfigurationManager.AppSettings["ida:SignUpRedirectUri"];
// B2C policy identifiers
public static string SignUpSignInPolicyId = ConfigurationManager.AppSettings["ida:SignUpSignInPolicyId"];
public static string EditProfilePolicyId = ConfigurationManager.AppSettings["ida:EditProfilePolicyId"];
public static string ResetPasswordPolicyId = ConfigurationManager.AppSettings["ida:ResetPasswordPolicyId"];
public static string DefaultPolicy = SignUpSignInPolicyId;
// API Scopes
public static string ApiIdentifier = ConfigurationManager.AppSettings["api:ApiIdentifier"];
public static string ReadTasksScope = ApiIdentifier + ConfigurationManager.AppSettings["api:ReadScope"];
public static string WriteTasksScope = ApiIdentifier + ConfigurationManager.AppSettings["api:WriteScope"];
public static string OpenIdScope = ApiIdentifier + OpenIdConnectScope.OpenId;
public static string ProfileScope = ApiIdentifier + "profile";
public static string OfflineAccessScope = ApiIdentifier + OpenIdConnectScope.OfflineAccess;
public static string UserImpersonationScope = ApiIdentifier + OpenIdConnectScope.UserImpersonation;
public static string[] Scopes = new string[] { ReadTasksScope, WriteTasksScope, OpenIdScope, ProfileScope, OfflineAccessScope, UserImpersonationScope };
// OWIN auth middleware constants
public const string ObjectIdElement = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier";
// Authorities
public static string Authority = String.Format(AadInstance, Tenant, DefaultPolicy);
public static GraphServiceClient graphClient = null;
readonly IAdminService _adminService = new AdminService();
/*
* Configure the OWIN middleware
*/
public void ConfigureAuth(IAppBuilder app)
{
// Required for Azure webapps, as by default they force TLS 1.2 and this project attempts 1.0
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
// Generate the metadata address using the tenant and policy information
MetadataAddress = String.Format(AadInstance, Tenant, DefaultPolicy),
// These are standard OpenID Connect parameters, with values pulled from web.config
ClientId = ClientId,
RedirectUri = RedirectUri,
PostLogoutRedirectUri = RedirectUri,
// Specify the callbacks for each type of notifications
Notifications = new OpenIdConnectAuthenticationNotifications
{
RedirectToIdentityProvider = OnRedirectToIdentityProvider,
AuthorizationCodeReceived = OnAuthorizationCodeReceived,
AuthenticationFailed = OnAuthenticationFailed
},
// Specify the claim type that specifies the Name property.
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name"
},
// Specify the scope by appending all of the scopes requested into one string (separated by a blank space)
Scope = $"openid profile offline_access {ReadTasksScope} {WriteTasksScope}"
}
);
}
/*
* On each call to Azure AD B2C, check if a policy (e.g. the profile edit or password reset policy) has been specified in the OWIN context.
* If so, use that policy when making the call. Also, don't request a code (since it won't be needed).
*/
private Task OnRedirectToIdentityProvider(RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
var policy = notification.OwinContext.Get<string>("Policy");
if (!string.IsNullOrEmpty(policy) && !policy.Equals(DefaultPolicy))
{
notification.ProtocolMessage.Scope = OpenIdConnectScope.OpenIdProfile + " " + OpenIdConnectScope.OfflineAccess + " " + OpenIdConnectScope.UserImpersonation;
notification.ProtocolMessage.ResponseType = OpenIdConnectResponseType.Code;
notification.ProtocolMessage.IssuerAddress = notification.ProtocolMessage.IssuerAddress.ToLower().Replace(DefaultPolicy.ToLower(), policy.ToLower());
}
return Task.FromResult(0);
}
/*
* Catch any failures received by the authentication middleware and handle appropriately
*/
private Task OnAuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
notification.HandleResponse();
// Handle the error code that Azure AD B2C throws when trying to reset a password from the login page
// because password reset is not supported by a "sign-up or sign-in policy"
if (notification.ProtocolMessage.ErrorDescription != null && notification.ProtocolMessage.ErrorDescription.Contains("AADB2C90118"))
{
// If the user clicked the reset password link, redirect to the reset password route
notification.Response.Redirect("/Account/ResetPassword");
}
else if (notification.Exception.Message == "access_denied")
{
notification.Response.Redirect("/");
}
else
{
notification.Response.Redirect("/Home/Error?message=" + notification.Exception.Message);
}
return Task.FromResult(0);
}
/*
* Callback function when an authorization code is received
*/
private Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedNotification notification)
{
// Extract the code from the response notification
var code = notification.Code;
string signedInUserID = notification.AuthenticationTicket.Identity.FindFirst(System.IdentityModel.Claims.ClaimTypes.NameIdentifier).Value;
TokenCache userTokenCache = new MSALSessionCache(signedInUserID, notification.OwinContext.Environment["System.Web.HttpContextBase"] as HttpContextBase).GetMsalCacheInstance();
ConfidentialClientApplication cca = new ConfidentialClientApplication(ClientId, Authority, RedirectUri, new ClientCredential(ClientSecret), userTokenCache, null);
try
{
AuthenticationResult result = cca.AcquireTokenByAuthorizationCodeAsync(code, Scopes).Result;
string token = result.AccessToken;
if (notification.AuthenticationTicket.Identity.FindFirst("newUser") != null)
{
var isNewUser = Convert.ToBoolean(notification.AuthenticationTicket.Identity.FindFirst("newUser").Value);
if (isNewUser)
{
SignUp(notification);
return Task.FromResult(0);
//notification.OwinContext.Response.Redirect("/Home/SignUp");
//notification.Options.RedirectUri = SignUpRedirectUri;
//notification.HandleResponse();
}
}
}
catch (Exception ex)
{
//TODO: Handle
// throw;
}
return Task.FromResult(0);
}
public void SignUp(AuthorizationCodeReceivedNotification notification)
{
string userId = notification.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
string fullName = notification.AuthenticationTicket.Identity.Name;
string firstName = (notification.AuthenticationTicket.Identity.FindFirst(ClaimTypes.GivenName) != null) ? notification.AuthenticationTicket.Identity.FindFirst(ClaimTypes.GivenName).Value : string.IsNullOrEmpty(fullName) ? string.Empty : fullName.Split(' ')[0];
string lastName = (notification.AuthenticationTicket.Identity.FindFirst(ClaimTypes.Surname) != null) ? notification.AuthenticationTicket.Identity.FindFirst(ClaimTypes.Surname).Value : fullName.Split(' ').Length<=1 ? string.Empty : fullName.Split(' ')[1];
string email = (notification.AuthenticationTicket.Identity.FindFirst("emails") != null) ?notification.AuthenticationTicket.Identity.FindFirst("emails").Value : string.Empty;
HubAccountDto account = new HubAccountDto();
account.Title = fullName + "_" + DateTime.UtcNow.Ticks;
account.Description = fullName + "_" + DateTime.UtcNow.Ticks;
account.CreatedOn = DateTime.UtcNow;
account.UpdatedOn = DateTime.UtcNow;
account.IsActive = true;
_adminService.CreateAccount(account);
HubUserDto user = new HubUserDto();
user.AccountId = account.Id;
user.Email = email;
user.ExternalUserId = userId;
user.FirstName = firstName;
user.LastName = lastName;
user.ImageUrl = null;
_adminService.CreateUser(user);
}
}`
AccountController.cs:
'public class AccountController : Controller
{
/*
* Called when requesting to sign up or sign in
*/
public void SignUpSignIn()
{
// Use the default policy to process the sign up / sign in flow
if (!Request.IsAuthenticated)
{
HttpContext.GetOwinContext().Authentication.Challenge();
return;
}
Response.Redirect("/");
}
/*
* Called when requesting to edit a profile
*/
public void EditProfile()
{
if (Request.IsAuthenticated)
{
// Let the middleware know you are trying to use the edit profile policy (see OnRedirectToIdentityProvider in Startup.Auth.cs)
HttpContext.GetOwinContext().Set("Policy", Startup.EditProfilePolicyId);
// Set the page to redirect to after editing the profile
var authenticationProperties = new AuthenticationProperties { RedirectUri = "/" };
HttpContext.GetOwinContext().Authentication.Challenge(authenticationProperties);
return;
}
Response.Redirect("/");
}
/*
* Called when requesting to reset a password
*/
public void ResetPassword()
{
// Let the middleware know you are trying to use the reset password policy (see OnRedirectToIdentityProvider in Startup.Auth.cs)
HttpContext.GetOwinContext().Set("Policy", Startup.ResetPasswordPolicyId);
// Set the page to redirect to after changing passwords
var authenticationProperties = new AuthenticationProperties { RedirectUri = "/" };
HttpContext.GetOwinContext().Authentication.Challenge(authenticationProperties);
return;
}
/*
* Called when requesting to sign out
*/
public void SignOut()
{
// To sign out the user, you should issue an OpenIDConnect sign out request.
if (Request.IsAuthenticated)
{
IEnumerable<AuthenticationDescription> authTypes = HttpContext.GetOwinContext().Authentication.GetAuthenticationTypes();
HttpContext.GetOwinContext().Authentication.SignOut(authTypes.Select(t => t.AuthenticationType).ToArray());
Request.GetOwinContext().Authentication.GetAuthenticationTypes();
}
}
}
}'
and I have another concern that is: Now I am having three apps two on .Net and one in angular, for .Net MVC apps both apps sharing their session for login and for logout working fine locally but after publishing the code on azure Single Single Out is not working as well
Please get me to the resolution for both of my concerns ASAP, it is really urgent
Hi @navyasric, is there anything to resolve this issue?
@vipswelt The msal-angular library cannot provide an single sign on experience without having access to an authenticated user session with AAD. You will need to provide a login hint and then call acquireTokenSilent for an API to avoid logging in again in the Angular app. You can get the login_hint from the idToken claims when user signs in to the 1st app. If you don't have any access to a sign in user's session or idToken, then you will have to prompt user to login in the Angular app before calling APIs.
Closing this issue with the above guidance.
I have two apps one in .net and another in angular, i am not able use single sign on with this package as it always promp for user name password page. How can i achieve that. Please reply with further information to acieve this. If it is not possible please reply me as well
Its really urgent for me.