Can't authenticate to KeyVault - No credential in this chain provided a token - azure-sdk-python

I'm trying to authenticate to a Key Vault using DefaultAzureCredential object with username/password authentication, but I'm getting this error:
[06/10/2020 13:57:37] Exception: ClientAuthenticationError:
[06/10/2020 13:57:37] No credential in this chain provided a token.
[06/10/2020 13:57:37] Attempted credentials:
[06/10/2020 13:57:37] EnvironmentCredential: Authentication failed: Unable to find wstrust endpoint from MEX. This typically happens when attempting MSA accounts. More details available here. https://github.com/AzureAD/microsoft-authentication-library-for-python/wiki/Username-Password-Authentication
[06/10/2020 13:57:37]
[06/10/2020 13:57:37] Please visit the documentation at
[06/10/2020 13:57:37] https://aka.ms/python-sdk-identity#defaultazurecredential
[06/10/2020 13:57:37] to learn what options DefaultAzureCredential supports
I can confirm that the required environment variables are being loaded from local.settings.json:
AZURE_CLIENT_ID
AZURE_USERNAME
AZURE_PASSWORD
Relevant code:
def encrypt(text):
uri = os.environ['KEYVAULT_URI']
credential = DefaultAzureCredential()
key_client = KeyClient(vault_url=uri, credential=credential)
key = key_client.get_key("managed-key")
crypto_client = CryptographyClient(key, credential=credential)
plaintext = text.encode()
return crypto_client.encrypt(EncryptionAlgorithm.rsa_oaep, plaintext)
local.settings.json looks like this:
{
"IsEncrypted": false,
"Values": {
"FUNCTIONS_WORKER_RUNTIME": "python",
"AzureWebJobsStorage": "{AzureWebJobsStorage}",
"KEYVAULT_URI": "<keyvault_uri>",
"AZURE_CLIENT_ID": "<client_id>",
"AZURE_USERNAME": "<email>",
"AZURE_PASSWORD": "<password>"
}
}

After writing the question I read the docs and found out the reason. Also the error trace is clear:
This typically happens when attempting MSA accounts.
Can't use a personal Microsoft account for this. Only work and school accounts would do.
From Username Password Authentication:
By design and policy, the username/password authentication works only
for Work and school accounts, but not for Microsoft Accounts (MSA).
I switched to service principal with secret and it solved my problem.
Details on options available at EnvironmentCredential.
"AZURE_TENANT_ID": "",
"AZURE_CLIENT_ID": "",
"AZURE_CLIENT_SECRET": ""

I found that upgrading my python package resolved this issue for me.
pip install --upgrade azure-identity
I had 1.2.0, and upgraded to 1.4.0.

Related

Token retrieval error after changing password

We are using Azure AD B2C Custom policies with Microsoft Azure Active Directory to authenticate users. We implemented the password change policy as given in the example below.
https://github.com/Azure-Samples/active-directory-b2c-custom-policy-starterpack/tree/main/scenarios/password-change
We created a link the user can visit and change his password. Once the password is changed, the user is redirected to the application again.
However, in some cases, we get the following error when the user is redirected to the application after changing the password.
{
"error": "invalid_grant",
"error_description": "AADB2C90088: The provided grant has not been issued for this endpoint. Actual Value : B2C_1A_Dev_SignupSignin and Expected Value : B2C_1A_Dev_PasswordChange\r\nCorrelation ID: b3a2fdd5-df58-4aa1-8eca-d91503ebb75a\r\nTimestamp: 2022-08-31 12:23:48Z\r\n"
}
This error does not happen to all users. But for some users, it happens always.
We use MSAL Angular library with the following versions.
azure/msal-angular": "^2.1.1"
azure/msal-browser": "^2.22.0"
We appreciate any help to resolve this issue.

How to get username / userinfo in Spring OAuth2 Resource Server

I have an api which uses AD Token for authorization.
I am trying to fetch the username of the user inside my service component. But im failing to. I have tried this.
val authentication: Authentication = SecurityContextHolder.getContext().authentication
println(authentication.name) // Random short string with 3 "-". Not JWT
println(authentication.details.toString()) // WebAuthenticationDetails [RemoteIpAddress=127.0.0.1, SessionId=null]
println(authentication.authorities.toString()) // Prints Scope [SCOPE_User.Read]
println(authentication.principal) // org.springframework.security.oauth2.jwt.Jwt#xxxxxxxx
The token is from AD and it does contain userdata. The payload contains these fields with user related stuff. I removed the rest.
{,
"family_name": "Wick",
"given_name": "John",
"name": "WickJohn",
"roles": [
"User"
],
"scp": "User.Read",
"unique_name": "wickjo#gmail.com",
"upn": "wickjo#gmail.com",,
}
Anyone have any idea?
I solved it easily just reading the jwt manually.
val authentication = SecurityContextHolder.getContext().authentication
val jwt = authentication.principal as Jwt
println(jwt.claims["name"])
Still would be interesting to find out why i didnt get it automatically
To get username/user info in Spring OAuth2 Resource Server, please try the below:
Make sure to configure resource server in the Authorization Server too.
To get user info by token, resource server provides a filter OAuth2AuthenticationProcessingFilter
The generated token is set into SecurityContextHolder.
Otherwise, When accessing userinfo try including the access token in the header (Authorization Bearer).
If the above doesn't work, then try using On Behalf Of Flow and the code mentioned in this GitHub blog.
For more information, please refer below links:
Azure AD 2.0 Troubleshooting – OAuth Architecture Guidance (authguidance.com)
How to get userinfo by jwt token in spring security oauth2 authorization server? - Stack Overflow

DRF and Oauth2: creating resource and authorization servers

I have developed a blog like project on the django rest framework and oauth2. I am now trying to separate the resource and authentication servers as shown here: https://django-oauth-toolkit.readthedocs.io/en/latest/resource_server.html
I have taken the following steps:
set up the auth server as described in the docs
added the below to settings.py in the auth server
OAUTH2_PROVIDER = {
'SCOPES': {'users': 'user details', 'read': 'Read scope', 'write': 'Write scope', 'groups': 'Access to your groups', 'introspection': 'introspection'},
'ACCESS_TOKEN_EXPIRE_SECONDS': 86400, # 1 Day.
}
set up the resource server as described in the docs
added this to settings.py in the resource server:
OAUTH2_PROVIDER = {
'RESOURCE_SERVER_INTROSPECTION_URL': 'http://localhost/o/introspect/',
'RESOURCE_SERVER_AUTH_TOKEN': 'abc',
}
I created the RESOURCE_SERVER_AUTH_TOKEN based on instructions here: Django OAuth- Separate Resource and Authorization Server
To summarise, I created a superuser for the resource server then added an application to the resource server using the admin site, choosing confidential for client type and authorization code for authorization grant type. 'abc' was the random string I chose for the access token.
Nevertheless, I am still facing the following error:
Introspection: Failed to get a valid response from the authentication server. Status code: 403, Reason: Forbidden.
NoneType: None
Do you have any idea of where I may be going wrong from what I've described? Have I understood this correctly and created the RESOURCE_SERVER_AUTH_TOKEN in the correct manner?
I had the same problem when using the 'RESOURCE_SERVER_AUTH_TOKEN'. So instead I used the client_id and client_secret.
Go ahead and try the following:
OAUTH2_PROVIDER = {
'RESOURCE_SERVER_INTROSPECTION_URL': 'http://127.0.0.1:8000/o/introspect/',
'RESOURCE_SERVER_INTROSPECTION_CREDENTIALS':
(
client_id,
client_secret
),
}
That is how it worked for me.

Accessing GitLab via API with Ruby

Trying to access my university's GitLab for the first time via API (I'm the repo owner but have no access to the console and can not edit config files or restart the server), I seem to lack some mandatory basics. Tried to use Ruby (no rails) with the Narkoz API wrapper. Unfortunately, there are no tutorials available which explain the very first step:
How to connect to the server and authenticate using UID+passwd?
Can anybody explain the process in human-readable instructions? The GitLab Manual was not helpful for me.
I hope that I could then figure out how to add GitLab users to my repository. Don't want to add 100 users using the web interface.
The manual entry you linked is for signing into GitLab with a third-party OAuth provider, which doesn't sound like what you're trying to do. What it sounds like you're trying to do is request an OAuth token which you can then use to access GitLab's API.
From the documentation:
In this flow, a token is requested in exchange for the resource owner credentials (username and password). The credentials should only be used when there is a high degree of trust between the resource owner and the client (e.g. the client is part of the device operating system or a highly privileged application), and when other authorization grant types are not available (such as an authorization code).
Which sounds like what you're trying to do.
One important thing of note from the documentation:
Deprecation notice: Starting in GitLab 8.11, the Resource Owner Password Credentials has been disabled for users with two-factor authentication turned on. These users can access the API using personal access tokens instead.
If this is the case for you, the following won't work and you'll need to generate an access token instead.
1. Requesting access token
POST request to /oauth/token with parameters:
{
"grant_type" : "password",
"username" : "user#example.com",
"password" : "secret"
}
Then, you'll receive the access token back in the response:
{
"access_token": "1f0af717251950dbd4d73154fdf0a474a5c5119adad999683f5b450c460726aa",
"token_type": "bearer",
"expires_in": 7200
}
You would then assign this token as your GitLab.private_token.
For the records -- what would have helped quicker is a minimum viable example -- especially since the API documentation contains known and not fixed errors.
host = myGitlabServer
require 'oauth2'
client = OAuth2::Client.new(clientIdFromGitLab, clientSecretFromGitLab, :site => host)
accessToken = client.password.get_token(myGitLabUid, myGitLabPassword)
require 'httparty'
# get own user data
response = HTTParty.get(host + '/api/v4/user?access_token=' + accessToken.token)
puts response.parsed_response
# get user data by user name
response = HTTParty.get(host + '/api/v4/users?username=' + username + '&access_token=' + access_token.token)
puts response.parsed_response[0]
# add user as reporter to project
response = HTTParty.post(host + '/api/v4/projects/' + projectId + '/members/?user_id=' + newUID + '&access_level=20&access_token=' + access_token.token)
puts response.parsed_response
or, you can use the excellent gitlab gem.
https://github.com/narkoz/gitlab

Another 401 when impersonating super-admin using service account with Admin SDK

I'm attempting to access the Admin SDK Directory API using a service account with domain wide delegation and the REST API. I can obtain a bearer token successfully when not impersonating a user, but with I attepmt to impersonate a user with a "sub" key, I receive a 401, "unauthorized_client" error. From everything I've read online (both in numerous SO answers & elsewhere), this would indicate that I haven't approved the application in my GSuite Admin console, but this is not the case.
Here are the steps I've taken so far.
1) Enable the Admin SDK
2) In IAM & Admin area, create a service account with DwD.
3) In Apis & Services area, select credentials and create service account key for the service account I just created.
4) Download the private key file.
{
"type": "service_account",
"project_id": "gsuite-REDACTED",
"private_key_id": "------3d2e",
"private_key": "REDACTED",
"client_email": "REDACTED-320#gsuite-REDACTED.iam.gserviceaccount.com",
"client_id": "-----0381",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://accounts.google.com/o/oauth2/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/REDACTED-320%40gsuite-REDACTED.iam.gserviceaccount.com"
}
5) Enable API Access in admin.google.com
6) In "Manage API client access" add the Service Account ID and the following scope for the client created in (3)
7) Ensure that the user I plan to impersonate is a "super-admin"
When I attempt to generate the bearer token I receive a 401 error
{
"error": "unauthorized_client",
"error_description": "Client is unauthorized to retrieve access tokens using this method."
}
I've tried using various libraries but always with the same result, so I'm guessing this has something to do with my app configuration rather than my code implementation. Would appreciate any help. For example, here I am using Ruby:
require 'googleauth'
scopes = ['https://www.googleapis.com/auth/admin.directory.user',
'https://www.googleapis.com/auth/admin.directory.group']
authorizer = Google::Auth::ServiceAccountCredentials.make_creds(
json_key_io: File.open("gsuite.json"),
scope: scopes).dup
authorizer.sub = 'joe#-----.io'
puts authorizer.fetch_access_token!
I figured out the problem - it was configuration not code. In Step 6, when I was autorizing the API client, I was entering the "Service account ID", in my case: "REDACTED-320#gsuite-REDACTED.iam.gserviceaccount.com". Although this appears to work - the correct numerical ID appears (as shown in screenshot) - I was getting 401 errors.
However, when I entered the "client_id" from my private key file, in my case a number ending in 0381, and used the same scope, THEN I could authenticate successfully.
It's frustrating that entering the service account id didn't throw an error., but all's well that ends well.
How long have you waited after step 6? Getting access after it's entered into the admin console can sometimes take a few hours.

Resources