OntoText GraphDb OAuth2 login in using Azure - oauth-2.0

I am trying to integrate AzuerAD OAuth authentication with graph-db following the instructions https://graphdb.ontotext.com/documentation/free/access-control.html and can't understand the reason it is failing.
My graphdb.properties config is as follows:
graphdb.auth.methods = basic, gdb, openid
graphdb.auth.openid.issuer = https://login.microsoftonline.com
graphdb.auth.openid.client_id = 596251be-....from AD
graphdb.auth.openid.username_claim = email
graphdb.auth.openid.auth_flow = code
graphdb.auth.openid.token_type = access
Also had failed attempted with:
graphdb.auth.openid.issuer = https://login.microsoftonline.com/da0adc10-UUID/oauth2/v2.0/token
I can successfully login to:
https://login.microsoftonline.com/da0adc10-UUID/oauth2/v2.0/authorize?client_id=596251be-from-AD&scope=openid&response_type=code. It's a success because after signing it prompts me with a reminder for a setting.
However, when the redirection happens to http://localhost:7200/login, it fails. (Also tried setting redirect uri to http://localhost:7200/login which was also a failure).
Failure (seen on Network tab of browser):
http://localhost:7200/login?code=0.AUIAENwK2txuskGrQ9UmuxHFYL5RYlkgP---lenghty-stuff---&session_state=dd3f6443-some-string
...eventually...
Request URL: http://localhost:7200/rest/security/authenticatedUser
Request Method: GET
Status Code: 401
Can you provide any guidance?

The redirect URL should be the basic URL of the GraphDB server, which in your case seems to be http://localhost:7200/. It won't work with http://localhost:7200/login.

Related

What auth flow to use with spa and service account msal

There's so many different flows in the Microsoft docs that I have no clue what one is needed for me. I am using React and Python. (I understand node, so if someone explains using node/express its fine)
What user should see:
A page with a button to login, nav is there but wont work till logged in. The login creates a popup to sign in with Microsoft account. Once signed in, the user will be able to use nav to see dynamics information.
What I am trying to do:
This app needs to sign in a user and obtain the users email through 'https://graph.microsoft.com/v1.0/me'.(no client secrets needed) Then I need to send that email in this request;
(The tenant == {company}.crm.dynamics.com.)
allInfo = requests.get(
f'https://{TENANT}api/data/v9.0/company_partneruserses?$filter=company_email eq \'{email}\'', headers=headers).json()
This backend request needs to have a client secret to obtain the information. So I believe my backend also needs to be logged on to a service account. I believe I need to get a token for my backend to make requests on behalf of the service account.
What I have:
I have a React frontend that is signing a user in and calling 'https://graph.microsoft.com/v1.0/me' correctly and getting that email. Once I get the email, I am sending it to my backend.
Now I have no clue how to proceed and have tried many things.
What I have tried for backend:
Attempt 1: I get a token but error: {'error': {'code': '0x80072560', 'message': 'The user is not a member of the organization.'}}. Problem is, this id is the Azure AD ID. It should def work
#app.route('/dynToken', methods=['POST'])
def get_dyn_token():
req = request.get_json()
partnerEmail = req['partnerEmail']
token = req['accessToken']
body = {
"client_id": microsoft_client_id,
"client_secret": client_secret,
"grant_type": "client_credentials",
"scope": SCOPE_DYN,
}
TENANTID = '{hash here}'
res = requests.post(
f'https://login.microsoftonline.com/{TENANTID}/oauth2/v2.0/token', data=body).json()
dyn_token = res['access_token']
headers = {
"Prefer": "odata.include-annotations=\"*\"",
"content-type": "application/json; odata.metadata=full",
"Authorization": f"Bearer {dyn_token}"
}
try:
allInfo = requests.get(
f'https://{TENANT}api/data/v9.0/company_partneruserses?$filter=company_email eq \'{email}\'', headers=headers).json()
print(allInfo)
Attempt 2:
Same code but instead of f'https://login.microsoftonline.com/{TENANTID}/oauth2/v2.0/token' its
f'https://login.microsoftonline.com/common/oauth2/v2.0/token'. Error: An exception occurred: [Errno Expecting value] : 0. Because it returns an empty string.
Now I don't know if I am even on the right path or where to go. I know the routes work themselves if the token is correct. I used only SSR with no react and these routes work. But I need the React to be there too. I just don't know what flow to use here to get what I need. The docs make it easy for /me route to work. But the {company}crm.dynamics.com docs don't really provide what I am trying to do.
Additional info after comment:
What 'f'https://{TENANT}api/data/v9.0/company_partneruserses?$filter=company_email eq '{email}'', headers=headers" is trying to get are API keys. Full code :
try:
allInfo = requests.get(
f'https://{TENANT}api/data/v9.0/company_partneruserses?$filter=company_email eq \'{email}\'', headers=headers).json()
partner_value = allInfo['value'][0]['_company_partner_value']
response = requests.get(
f'https://{TENANT}api/data/v9.0/company_partnerses({partner_value})', headers=headers).json()
return {'key': response['company_apikey'], 'secret': response['company_apisecret']}
Then once it has the keys:
def api_authentication(apikey, apisecret):
headers = get_headers() #<-- same headers as above with using dyn_token
response = requests.get(
f'https://{TENANT}api/data/v9.0/company_partnerses?$filter=company_apikey eq \'{apikey}\' and company_apisecret eq \'{apisecret}\'&$select=company_apikey,company_apisecret,_company_account_value,_company_primarycontact_value,blahblah_unassignedhours,company_reporturl', headers=headers).json()
return response
Afterwards I am able to get all the information I am looking for to send back to my frontend for the client to see. (By making multiple request to crm with these keys)
The client_credentials grant that you are using should work, provided the CRM trusts the token issued to the client (your python backend). Please use MSAL library instead of hand crafting the token request. It will save you time and eliminate errors.

Snowflake custom OAuth client fails with invalid_client error?

I have created a custom OAuth client for the snowflake account by referring documentation here, https://docs.snowflake.com/en/user-guide/oauth-custom.html
I created an OAuth custom instance for my local using the following query:
create security integration My_Snowflake_Connector
type = oauth
enabled = true
oauth_client = custom
oauth_client_type = 'CONFIDENTIAL'
oauth_redirect_uri = 'http://localhost:4200/api/auth/callback/snowflake'
oauth_issue_refresh_tokens = true
oauth_refresh_token_validity = 86400
blocked_roles_list = ()
pre_authorized_roles_list = ('SYSADMIN', 'ACCOUNTADMIN', 'SECURITYADMIN')
oauth_allow_non_tls_redirect_uri = true
I have obtained authorization & token URLs using the following query:
DESCRIBE security integration My_Snowflake_Connector
I also obtained secrets using the following query:
SELECT SYSTEM$SHOW_OAUTH_CLIENT_SECRETS( 'MY_SNOWFLAKE_CONNEECTOR' )
I have used passport-oauth2 plugin & OAuth2Strategy
On initiating OAuth flow I am rightly taken to the snowflake account I log in it shows the OAuth consent screen but upon redirection, I get an error invalid_client.
I am getting the following error JSON blob:
{\n "data" : null,\n "error" : "invalid_client",\n "code" : null,\n "message" : "This is an invalid client.",\n "success" : false,\n "headers" : null\n}
I have verified the callback URL, client id & secret none seems to be wrong.
What may be wrong with my configuration?
Update
We could not figure out the reason behind the OAuth error, Finally, we ended up using the Node js client from snowflake. For more info: https://docs.snowflake.com/en/user-guide/nodejs-driver.html
I could see a similar error when testing OAuth2 from Postman when Client Authentication is set to "Send Client Credentials in body".
On changing this to "Send as Basic Auth Header",token generation works fine and proceeds successfully.
I guess this change should resolve the issue in your case as well. One of the reference that I checked is here : https://github.com/ciaranj/node-oauth/pull/316

LinkedIn API Get Access Token Failed

We are trying to implement the LinkedIn API authentication module based on: https://learn.microsoft.com/en-us/linkedin/shared/authentication/authorization-code-flow?context=linkedin/context.
We have the redirect url for the application setup as our company's main page (https://www.{site}.com) and we are able to get the auth code from the redirect URL. However, it gives us 401 error below when exchange for access token:
b'{"error":"invalid_request","error_description":"Unable to retrieve
access token: authorization code not found"}'
The weird thing is, it works and we are able to exchange the code for access token if we switch the redirect url to a different site like https://www.example.com in the API Console. Below is the Py3 code we use:
from requests_oauthlib import OAuth2Session
from requests_oauthlib.compliance_fixes import linkedin_compliance_fix
# Credentials and redirect uri you get from registering a new application
client_id = 'client_id'
client_secret = 'client_secret'
redirect_url = 'redirect_url'
# OAuth endpoints given in the LinkedIn API documentation (check for updates)
authorization_base_url = 'https://www.linkedin.com/oauth/v2/authorization'
token_url = 'https://www.linkedin.com/oauth/v2/accessToken'
# Authorized Redirect URL (from LinkedIn config)
o2_session = OAuth2Session(client_id=client_id, redirect_uri=redirect_url, scope=['rw_ads', 'r_ads_reporting'])
linkedin = linkedin_compliance_fix(o2_session)
# Redirect user to LinkedIn for authorization
authorization_url, state = linkedin.authorization_url(authorization_base_url)
print('Please go here and authorize,', authorization_url)
# Get the authorization verifier code from the callback url
redirect_response = input('Paste the full redirect URL here:')
linkedin.fetch_token(token_url, include_client_id=client_id, client_secret=client_secret, authorization_response=redirect_response)
token = linkedin.access_token
Understood that the auth code has short life span, so tried both redirect URL seconds after the code is post back to the URL. Can anyone think of any reason could cause this weird different behaviors for different redirect URLs.

What is the best way to dynamically specify the redirect url for OAuth strategies in passport.js?

I have setup my facebook auth per passportjs docs:
var passport = require('passport')
, FacebookStrategy = require('passport-facebook').Strategy;
passport.use(new FacebookStrategy({
clientID: FACEBOOK_APP_ID,
clientSecret: FACEBOOK_APP_SECRET,
callbackURL: "http://www.example.com/facebook/callback"
},
function(accessToken, refreshToken, profile, done) { ... });
}
));
app.get('/login/facebook', passport.authenticate('facebook'))
.get('/facebook/callback', passport.authenticate('facebook', {successRedirect: '/', failureRedirect: '/login'}));
All this works fine. However, there are cases (such as token expiration) when I want to automatically redirect the user to the page that the user was on before initiating the login request. So I tried to plumb a query string param through the login request (from client to server to facebook and back). But I cant see a way to specify that in the callbackURL.
Furthermore, when I tried hard-coding some context param to the config callbackURL (eg: "http://www.example.com/facebook/callback?redir=lastUserPage") I get an OAuth parse error. Interestingly enough, Facebook does respond correctly with the access code as well as the redir param, but it fails with OAUTH exception:
FacebookTokenError: Error validating verification code. Please make sure your redirect_uri is identical to the one you used in the OAuth dialog request
at Strategy.parseErrorResponse (C:\Sources\node_modules\passport-facebook\lib\strategy.js:198:12)
at Strategy.OAuth2Strategy._createOAuthError (C:\Sources\node_modules\passport-facebook\node_modules\passport-oauth2\lib\strategy.js:345:16)
at C:\Sources\node_modules\passport-facebook\node_modules\passport-oauth2\lib\strategy.js:171:43
at C:\Sources\node_modules\passport-facebook\node_modules\passport-oauth2\node_modules\oauth\lib\oauth2.js:177:18
at passBackControl (C:\Sources\node_modules\passport-facebook\node_modules\passport-oauth2\node_modules\oauth\lib\oauth2.js:124:9)
at IncomingMessage.<anonymous> (C:\Sources\node_modules\passport-facebook\node_modules\passport-oauth2\node_modules\oauth\lib\oauth2.js:143:7)
at IncomingMessage.emit (events.js:117:20)
at _stream_readable.js:943:16
at process._tickCallback (node.js:419:13)
Note that I had this working using WIF before. I don't see any security concerns with passing additional query string parameters through the OAuth process..
Any idea how I can get past this?
I'm not sure how to do what you're asking, but for your desired end goal you could:
Save a cookie before authenticating
Authenticate the user
on the resulting callback page, check for the cookie and redirect if present.
Wouldn't this work just as easily?

Withings API Status Code 2555

I'm trying to integrate Withings with a rails apps. I'm using an Omniauth provider someone wrote called omniauth-withings. I was able to configure the provider to allow me to visit /auth/withings which redirects to the Withings authorization page. After I allow access, the browser is redirected to the callback url /auth/withings/callback. I have this routed to a controller action that attempts to get the measurement data from Withings using the simplificator-withings gem.
Withings.consumer_secret = ENV['withings_app_key']
Withings.consumer_key = ENV['withings_app_secret']
auth_hash = request.env['omniauth.auth']
user_id = auth_hash.extra.raw_info.body.users.first.id
withings_user = User.authenticate(user_id, auth_hash.credentials.token, auth_hash.credentials.secret)
measurements = withings_user.measurement_groups(:device => Withings::SCALE)
The problem happens when I call User.authenticate(), I get this:
An unknown error occurred - Status code: 2555
Is there something I'm missing here?
I was getting the same error with a django app. It turns out I was using the wrong token and secret. I was using the oauth_token and oauth_token_secret returned from step 1 of the authorization process, rather than the oauth_token and oauth_token_secret from step 3. Make sure you are using the values from step 3. The API documentation shows the same values returned from these calls, but they will be different. Hopefully this helps you too.

Resources