Getting unauthorized access error when implementing SAML 2.0 based applications - reactjs

I am getting this error when clicking on Login with Microsoft button
We're unable to complete your request
unauthorized_client: The client does not exist or is not enabled for consumers.
If you are the application developer, configure a new application through the App Registrations in the Azure Portal at https://go.microsoft.com/fwlink/?linkid=2083908.
I am working on Front-End and I am also passing the correct client-id but still getting this error
Here is the code --
const App = () => {
const loginHandler = (err, data, msal) => {
console.log(err, data);
// some actions
if (!err && data) {
// onMsalInstanceChange(msal);
console.log(msal);
}
};
return (
<div className="app">
<MicrosoftLogin clientId={config.clientId} authCallback={loginHandler} />
</div>
);
};

It looks like you are using OAuth2 / OpenID Connect to login with an application that uses SAML?
You need to create an enterprise application.

Related

Electron App with Azure AD - without Interactive browser

I am trying to integrate Azure AD authentication with my Electron App (with Angular). I took reference from this link and able to integrate: https://learn.microsoft.com/en-us/azure/active-directory/develop/tutorial-v2-nodejs-desktop
Issue: It's using getTokenInteractive() method and it's navigating to an external browser. As per my requirement we don't have to navigate to an external browser, it should open the UI inside my electron App where end users can provide their credentials.
Another option if possible we can open the Azure AD url part of my electron App.
I took reference from this link and able to integrate: https://learn.microsoft.com/en-us/azure/active-directory/develop/tutorial-v2-nodejs-desktop
async getTokenInteractive(tokenRequest) {
try {
const openBrowser = async (url) => {
await shell.openExternal(url);
};
const authResponse = await this.clientApplication.acquireTokenInteractive({
...tokenRequest,
openBrowser,
successTemplate: '<h1>Successfully signed in!</h1> <p>You can close this window now.</p>',
errorTemplate: '<h1>Oops! Something went wrong</h1> <p>Check the console for more information.</p>',
});
return authResponse;
} catch (error) {
throw error;
}
}

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.

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>
)

MSAL SSO with Microsoft Teams Tabs

Hi So I'm using MSAL to authenticate my users, It's working in my browser but I want to embed my web in Microsoft teams tabs and use the SSO. If I see on the MSAL documentation https://learn.microsoft.com/en-us/azure/active-directory/develop/msal-js-sso I can use AcquireTokenSilent and using sid to authenticate. but I don't know why I get this error Unhandled Rejection (ClientAuthError): Token renewal operation failed due to timeout after calling the AcquireTokenSilent.
async componentDidMount() {
var needAuth = true;
microsoftTeams.initialize();
await microsoftTeams.getContext(async function (context) {
alert(JSON.stringify(context));
needAuth = false;
const provider = {
scopes: ["https://graph.microsoft.com/.default", "user.read"],
sid: context.sessionId,
extraQueryParameters: { domain_hint: 'organizations' }
};
await authProvider.acquireTokenSilent(provider);
alert(authProvider.authenticationState);
this.setState({ needLogin: needAuth });
})
}
Is there anything wrong with my code? Am I missing something after AcquireTokenSilent?

Sending Azure token to API back-end keeps returning 401 Unauthorzied error when OnTokenValidated is added to the API

Recently we have created a React front-end which communicates with our API back-end following this tutorial: https://itnext.io/a-memo-on-how-to-implement-azure-ad-authentication-using-react-and-net-core-2-0-3fe9bfdf9f36
Just as in the tutorial we have set-up the authentication in the front-end with the adal-react library. We added/registered the front-end in azure.
Next we created our API (.Net Core 2) and also registered this in the azure environment, the config is setup in the appsettings:
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantDomain": "our_azure_environment.onmicrosoft.com",
"TenantId": "our_azure_environment.onmicrosoft.com",
"ClientId": "our_front-end_azure_id_1234"
}
In the API we also added the JWT middleware in the ConfigureServices as follow:
services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.Audience = Configuration["AzureAd:ClientId"];
options.Authority = $"{Configuration["AzureAd:Instance"]}{Configuration["AzureAd:TenantId"]}";
});
When testing (calling an endpoint from the front-end) after logging in the front-end works, the data is being returned and the user is authenticated (api endpoint has the Authorize attribute), when not logged in the api endpoint returns 401 (as it should).
The problem is as follows:
When I add the following piece of code to the API ConfigureServices (which I want to use to do some additional stuff after authenticating) :
options.Events = new JwtBearerEvents()
{
OnTokenValidated = context =>
{
//Check if user has a oid claim
if (!context.Principal.HasClaim(c => c.Type == "oid"))
{
context.Fail($"The claim 'oid' is not present in the token.");
}
return Task.CompletedTask;
}
};
suddenly, the calls to the API endpoint return a 401 (Unauthorized) error when logged in.. Though, if I remove the OnTokenValidated part it works fine.
When reaching the OnTokenValidated, the token should already be validated / authenticated or am I wrong?
IntelliSense also says; Invoked after the security token has passed validation and a ClaimsIdentity has been generated.
Did I forgot to add some setting? My feeling tells me that it is propably a wrong setup in azure itself but I have actually no clue.
The same token which is send from the front-end to the API is also being send to the graph API, when doing this, graph asks to give consent and after agreeing it works. With this in mind I believe I should add some permission to the API or something but I am not sure.
UPDATE
juunas pointed out in his comment below that I was using the wrong ClaimsPrincipal value this fixed the initial problem but now the following gave me the 401 error:
In my ConfigureServices (before the AddAuthentication part) I have added the following to manage / add users to my AspNetUsers table (in my azure database):
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<TRSContext>()
.AddDefaultTokenProviders();
When adding this code to the pipeline, I once more get the 401 error in the front-end. Any clue why this is?
UPDATE2
I found the solution for above (update). This was caused due to AddIdentity taken over the Authentication from JWT. This can be avoided by adding:
Options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
Options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
to .AddAuthentication options:
services.AddAuthentication(Options =>
{
Options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
Options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
Options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
More information about the above can be found here:
https://github.com/aspnet/Identity/issues/1376
The error appears in the first case due to the fact that .NET ClaimsPrincipal objects translate the oid claim type to: http://schemas.microsoft.com/identity/claims/objectidentifier.
So it needs to be like:
options.Events = new JwtBearerEvents()
{
OnTokenValidated = context =>
{
//Check if user has a oid claim
if (!context.Principal.HasClaim(c => c.Type == "http://schemas.microsoft.com/identity/claims/objectidentifier"))
{
context.Fail($"The claim 'oid' is not present in the token.");
}
return Task.CompletedTask;
}
};

Resources