Setting up Azure B2C with React - reactjs

I am trying to configure my react/.NET 5.0 application to work with Azure B2C. I have everything set up , I have tried to run this against an MVC application and I get the login screen. But for some reason, when I try to redirect from a react page, I keep getting the same error. There appears to be almost no real good documentation for this as well. This is my authConfig file.
export const msalConfig = {
auth: {
clientId: process.env.REACT_APP_ADB2C_CLIENT_ID
, authority: process.env.REACT_APP_ADB2C_AUTHORITY
, knownAuthorities: [process.env.REACT_APP_KNOWN_AUTH]
, clientSecret: process.env.REACT_APP_CLIENT_SECRET
, reponseType: 'code'
},
cache: {
cacheLocation: 'sessionStorage'
,storeAuthStateInCoolie: false
}
};
const b2cPolicies = {
name: {
signUpSignIn: "B2C_1_cp_signin_up"
, forgotPassword: "B2C_1_cp_forgot_pwd"
, editProfile: "B2C_1_cp_edit_profile"
},
authorities: {
signUpSignIn: {
authority: `https://${process.env.REACT_APP_TENANT_LOGIN}/${process.env.REACT_APP_TENANT}/${process.env.REACT_APP_SIGNUP_POLICY}`,
},
forgotPassword: {
authority: `https://${process.env.REACT_APP_TENANT_LOGIN}/${process.env.REACT_APP_TENANT}/${process.env.REACT_APP_FORGOT_POLICY}`,
},
editProfile: {
authority: `https://${process.env.REACT_APP_TENANT_LOGIN}/${process.env.REACT_APP_TENANT}/${process.env.REACT_APP_EDIT_POLICY}`
}
},
authorityDomain: process.env.REACT_APP_TENANT_LOGIN
}
export const loginRequest = {
scopes: ["openid", "profile"],
};
I keep running into this error when I click on the link to redirect.
Any help with this would be great.

The reply URL must begin with the scheme https. Please check if reply urls are configured correctly which must be same in azure portal and in code .
Check if the callback path is set to identity provider something like /signin-oidc for redirect url .(And make sure you have unique callback if multiple urls are used.
such as https://contoso.com/signin-oidc.
The CallbackPath is the path where server will redirect during authentication. It's automatically handled by the OIDC middleware itself, that means we can't control the logic by creating a new controller/action and set CallbackPath to it
If you have check marked id_token in portal, try redirecting to home page ,instead of api actions directly.
Change the cookies to be set as secure using this in start up class
services.Configure(options =>
{
options.CheckConsentNeeded = context => true;//add if consent needed
options.MinimumSameSitePolicy = SameSiteMode.None; // else try SameSiteMode.Lax;
options.Secure = CookieSecurePolicy.Always;
});
use Microsoft.AspNetCore.HttpOverrides; reference in startup.cs class, by including the nuget package.
Also check and Add > app.UseHttpsRedirection(); above app.authentication(); in startup configure method.
then try again.
If not try to set ProtocolMessage.RedirectUri to the HTTPS url
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(options =>
{
Configuration.Bind("AzureAdB2C", options);
options.Events = new OpenIdConnectEvents
{
OnRedirectToIdentityProvider = async ctx =>
{
/* Redirect Uri modified for https protocol */
ctx.ProtocolMessage.RedirectUri = urlWithHttps
}
}
});
Or you can pass login hint :Please refer this doc.
References:
Tutorial: Register an application - Azure AD B2C | Microsoft Docs
Configure authentication in a sample web application by using Azure Active Directory B2C | Microsoft Docs

Related

Django DRF + Allauth: OAuth2Error: Error retrieving access token on production build

We are integrating DRF (dj_rest_auth) and allauth with the frontend application based on React. Recently, the social login was added to handle login through LinkedIn, Facebook, Google and GitHub. Everything was working good on localhost with each of the providers. After the staging deployment, I updated the secrets and social applications for a new domain. Generating the URL for social login works fine, the user gets redirected to the provider login page and allowed access to login to our application, but after being redirected back to the frontend page responsible for logging in - it results in an error: (example for LinkedIn, happens for all of the providers)
allauth.socialaccount.providers.oauth2.client.OAuth2Error:
Error retrieving access token:
b'{"error":"invalid_redirect_uri","error_description":"Unable to retrieve access token: appid/redirect uri/code verifier does not match authorization code. Or authorization code expired. Or external member binding exists"}'
Our flow is:
go to frontend page -> click on provider's icon ->
redirect to {BACKEND_URL}/rest-auth/linkedin/url/ to make it a POST request (user submits the form) ->
login on provider's page ->
go back to our frontend page {frontend}/social-auth?source=linkedin&code={the code we are sending to rest-auth/$provider$ endpoint}&state={state}->
confirm the code & show the profile completion page
The adapter definition (same for every provider):
class LinkedInLogin(SocialLoginView):
adapter_class = LinkedInOAuth2Adapter
client_class = OAuth2Client
#property
def callback_url(self):
return self.request.build_absolute_uri(reverse('linkedin_oauth2_callback'))
Callback definition:
def linkedin_callback(request):
params = urllib.parse.urlencode(request.GET)
return redirect(f'{settings.HTTP_PROTOCOL}://{settings.FRONTEND_HOST}/social-auth?source=linkedin&{params}')
URLs:
path('rest-auth/linkedin/', LinkedInLogin.as_view(), name='linkedin_oauth2_callback'),
path('rest-auth/linkedin/callback/', linkedin_callback, name='linkedin_oauth2_callback'),
path('rest-auth/linkedin/url/', linkedin_views.oauth2_login),
Frontend call to send the access_token/code:
const handleSocialLogin = () => {
postSocialAuth({
code: decodeURIComponent(codeOrAccessToken),
provider: provider
}).then(response => {
if (!response.error) return history.push(`/complete-profile?source=${provider}`);
NotificationManager.error(
`There was an error while trying to log you in via ${provider}`,
"Error",
3000
);
return history.push("/login");
}).catch(_error => {
NotificationManager.error(
`There was an error while trying to log you in via ${provider}`,
"Error",
3000
);
return history.push("/login");
});
}
Mutation:
const postSocialUserAuth = builder => builder.mutation({
query: (data) => {
const payload = {
code: data?.code,
};
return {
url: `${API_BASE_URL}/rest-auth/${data?.provider}/`,
method: 'POST',
body: payload,
}
}
Callback URLs and client credentials are set for the staging environment both in our admin panel (Django) and provider's panel (i.e. developers.linkedin.com)
Again - everything from this setup is working ok in the local environment.
IMPORTANT
We are using two different domains for the backend and frontend - frontend has a different domain than a backend
The solution was to completely change the callback URL generation
For anyone looking for a solution in the future:
class LinkedInLogin(SocialLoginView):
adapter_class = CustomAdapterLinkedin
client_class = OAuth2Client
#property
def callback_url(self):
callback_url = reverse('linkedin_oauth2_callback')
site = Site.objects.get_current()
return f"{settings.HTTP_PROTOCOL}://{site}{callback_url}"
Custom adapter:
class CustomAdapterLinkedin(LinkedInOAuth2Adapter):
def get_callback_url(self, request, app):
callback_url = reverse(provider_id + "_callback")
site = Site.objects.get_current()
return f"{settings.HTTP_PROTOCOL}://{site}{callback_url}"
It is important to change your routes therefore for URL generation:
path('rest-auth/linkedin/url/', OAuth2LoginView.adapter_view(CustomAdapterLinkedin))
I am leaving this open since I think this is not expected behaviour.

identity server 4 + oidc-client-js logout redirect

I'm trying to implement OpenId and oidc-client-js in react. Right now I'm stuck in implementing logout function.
From what I understand, I need to set the post_logout_redirect_uri and use signoutRedirect() to logout the user. Logging out the user works, but unfortunately it stays in the identity server logout page. What I need to do is to redirect the user to the post_logout_redirect_uri.
Can someone tell me what am I missing here? Thanks in advance!
This is the URL where I get redirected. https://identityserver.xx.com/Account/Logout?logoutId=CfDJ8Cqm6alCoddAqWl...
Here's my tech stack:
Identity Server v4
oidc-client-js
ReactJS (TS) with mobx state manager.
Below is my Identity server admin settings.
Front Channel Logout Uri: http://localhost:3000/signout-oidc
Post Logout Redirect Uris: http://localhost:3000/signout-callback-oidc
Here's the logout code
logout = async () => {
try {
userManager.signoutRedirect({
id_token_hint: this.user?.id_token,
post_logout_redirect_uri : process.env.REACT_APP_LOGOFF_REDIRECT_URL,
state: this.user?.state
}).then( () => {
console.log('done signoutRedirect')
});
userManager.clearStaleState()
}catch(error){
console.log(error);
}
}
in AccountController -> BuildLoggedOutViewModelAsync method check AutomaticRedirectAfterSignOut is true when constructing the viewmodel.
var vm = new LoggedOutViewModel
{
AutomaticRedirectAfterSignOut = AccountOptions.AutomaticRedirectAfterSignOut, //this must return true.
PostLogoutRedirectUri = logout?.PostLogoutRedirectUri,
ClientName = string.IsNullOrEmpty(logout?.ClientName) ? logout?.ClientId : logout?.ClientName,
SignOutIframeUrl = logout?.SignOutIFrameUrl,
LogoutId = logoutId
};
in your view LoggedOut.cshtml check ~/js/signout-redirect.js is included properly.
#section scripts
{
#if (Model.AutomaticRedirectAfterSignOut)
{
<script src="~/js/signout-redirect.js"></script>
}
}
if your logged out page doesn't contain anchor tag with <a id="post-logout-redirect-uri" ...> you probably have mismatching post_logout_redirect_uri configured in request and client.

Amplify: 'No Cognito Federated Identity pool provided' with federation login

I'm using Amplify's React UI kit, and I'm trying to get my social logins working with Cognito.
At the moment, I'm getting No Cognito Federated Identity pool provided
Here's the code for the buttons:
<AmplifyAuthenticator federated={{
googleClientId:
'*id*',
facebookAppId: '*id*'
}} usernameAlias="email">
<AmplifySignUp headerText="Create Account" slot="sign-up"/>
<AmplifySignIn slot="sign-in">
<div slot="federated-buttons">
<AmplifyGoogleButton onClick={() => Auth.federatedSignIn()}/>
<AmplifyFacebookButton onClick={() => Auth.federatedSignIn()}/>
</div>
</AmplifySignIn>
</AmplifyAuthenticator>
I've tried making an identity pool, and filling in the authentication providers for Cognito, Google and Facebook and still getting the same error.
For the URI's in both the providers, I've included the domain address as a authorised javascript origin, and for the redirect, I added oauth2/idpresponse to the end of the domain.
This is working within the Amplify hosted UI, just not with my React solution.
On Cognito, my redirect is my domain /token. As I wait for Amplify to set the cookies before redirecting the user.
token.tsx
export default function TokenSetter() {
const router = useRouter();
useAuthRedirect(() => {
// We are not using the router here, since the query object will be empty
// during prerendering if the page is statically optimized.
// So the router's location would return no search the first time.
const redirectUriAfterSignIn =
extractFirst(queryString.parse(window.location.search).to || "") || "/";
router.replace(redirectUriAfterSignIn);
});
return <p>loading..</p>;
}
Here's my Amplify.configure()
Amplify.configure({
Auth: {
region: process.env.USER_POOL_REGION,
userPoolId: process.env.USER_POOL_ID,
userPoolWebClientId: process.env.USER_POOL_CLIENT_ID,
IdentityPoolId: process.env.IDENTITY_POOL_ID,
oauth: {
domain: process.env.IDP_DOMAIN,
scope: ["email", "openid"],
// Where users get sent after logging in.
// This has to be set to be the full URL of the /token page.
redirectSignIn: process.env.REDIRECT_SIGN_IN,
// Where users are sent after they sign out.
redirectSignOut: process.env.REDIRECT_SIGN_OUT,
responseType: "token",
},
},
});
In order to call an user pool federated IDP directly from your app you need to pass the provider option to Auth.federatedSignIn:
Auth.federatedSignIn({
provider: provider,
})
The options are defined in an enum.
export enum CognitoHostedUIIdentityProvider {
Cognito = 'COGNITO',
Google = 'Google',
Facebook = 'Facebook',
Amazon = 'LoginWithAmazon',
Apple = 'SignInWithApple',
}
Not exhaustive unfortunately. If you have a custom OIDC or SAML idp you use the provider name or id.
For Facebook the call looks like so:
Auth.federatedSignIn({
provider: 'Facebook',
})
Integrated with a button:
const FacebookSignInButton = () => (
<AmplifyButton
onClick={()=>Auth.federatedSignIn({provider: 'Facebook'})}>
Sign in with Facebook
</AmplifyButton>
)

Wrong version of access token (expect V2 , received V1)

I am trying to register users from the Azure Active directory using #azure/msal-angular, to be more precise I tried the following tutorial
Those are the changes I have made to the project
export function MSALInstanceFactory(): IPublicClientApplication {
return new PublicClientApplication({
auth: {
clientId: 'my_real_client_id,
redirectUri: 'http://localhost:4200',
authority: 'https://login.microsoftonline.com/my_real_tenant_id',
postLogoutRedirectUri: '/'
},
cache: {
cacheLocation: BrowserCacheLocation.LocalStorage,
storeAuthStateInCookie: isIE, // set to true for IE 11
},
system: {
loggerOptions: {
loggerCallback,
logLevel: LogLevel.Info,
piiLoggingEnabled: false
}
}
});
}
export function MSALInterceptorConfigFactory(): MsalInterceptorConfiguration {
const protectedResourceMap = new Map<string, Array<string>>();
protectedResourceMap.set('https://graph.microsoft.com/v1.0/me', ['user.read']);
protectedResourceMap.set('http://localhost:5000/', ['profile']);
return {
interactionType: InteractionType.Redirect,
protectedResourceMap
};
}
The problem is that MsalInterceptor adds V1 token to the URL for the request to my API which expects V2.
Azure is configured to accessTokenAcceptedVersion: 2
I can provide more information if needed
Update
In my case, the problem was due to the scopes specified, both API for "user.read" and "profile" require V1 accessToken
Although you have resolved the issue, I would like to clarify more details.
In fact it's not the permissions "user.read" and "profile" require V1 accessToken. The actual reason is that:
Access Tokens versions are determined by the configuration of your application/API in the manifest. You have to change the accessTokenAcceptedVersion part of the manifest to 2 if you want a V2 access token. This is also the reason why you cannot control what version of the access token you will get from your client application when you request an access token.
We can't modify the accessTokenAcceptedVersion from Microsoft Graph side.
So the conclusion is that an access token for MS Graph and those are always V1 access token.

How to get user profile details using Azure AD

I am using react-aad-masl module to integrate Microsoft SSO in my react application. I am able to redirect my user to Microsoft login page and after that its coming back to my page with token. After that its only returning accountInfo that contain User name and email ID. I have a requirement to get user other details as well like ID, first name, last name, Group, etc.
These all details will come through this Azure AD MASL or i need to do Graph API call?
Below is my implementation
//authProvider.js
import { MsalAuthProvider, LoginType } from 'react-aad-msal';
import { Logger, LogLevel } from "msal";
// Msal Configurations
const config = {
auth: {
authority: 'https://login.microsoftonline.com/*********',
clientId: '*************',
redirectUri: 'http://localhost:3000/'
},
// Enable logging of MSAL events for easier troubleshooting.
// This should be disabled in production builds.
system: {
logger: new Logger(
(logLevel, message, containsPii) => {
console.log("[MSAL]", message);
},
{
level: LogLevel.Verbose,
piiLoggingEnabled: false
}
)
},
cache: {
cacheLocation: "sessionStorage",
storeAuthStateInCookie: true
}
};
// Authentication Parameters
const authenticationParameters = {
scopes: [
'user.read',
'profile',
'openid'
//'profile.read',
// 'User.Read.All',
// 'Group.Read.All',
// 'User.ReadBasic.All',
// 'Group.Read'
]
}
// Options
const options = {
loginType: LoginType.Redirect,
tokenRefreshUri: window.location.origin + '/auth.html'
}
export const authProvider = new MsalAuthProvider(config, authenticationParameters, options)
//APP.js
<AzureAD provider={authProvider} forceLogin={false}>
{(abc) => {
console.log('>>>>>>>>>>>>...', abc);
props.setAccountInfo(abc.accountInfo.account);
return <AppRouter />
}}
</AzureAD>
Here in abc i am getting below information
Please help me in this
You need to configure the optional claims in the application token configuration for First Name, Last Name.
Please go through the documentation for more information
Regarding the group information please check this document

Resources