Okta Authentication works but Get User by Id gives Invalid Token Provided - okta

I have a Django app that authenticates using Okta:
headers = {
'Authorization': 'SSWS {}'.format(<okta api token>),
'Accept': 'application/json',
'Content-Type': 'application/json'
}
authentication_payload = {
'username': <username>,
'password': <password>
}
response = requests.post(
<okta auth endpoint>,
headers=headers,
data=json.dumps(authentication_payload)
)
This works successfully. From the response content I am able to get the User Id:
content = json.loads(r.content.decode('utf-8'))
okta_user_id = content['_embedded']['user']['id']
I then use the okta_user_id to create the endpoint to get the okta user by id:
okta_user_endpoint = https://<org>.okta.com/api/v1/users/<okta_user_id>
I then use the same headers from the authentication call, with the same api token, and try to get the user by id:
user_response = requests.get(
okta_user_endpoint,
headers=headers
)
But this is unsuccessful. I get a 401 error with the following content:
{
"errorCode":"E0000011",
"errorSummary":"Invalid token provided",
"errorLink":"E0000011",
"errorCauses":[]
}
Seems straight forward with an invalid token, but if the token is invalid how am I able to successfully make the authentication call? And if the token if valid for the authentication call why is it not working to get the user by id?

Okta recently changed the way that the /authn endpoint works. The /authn endpoint no longer requires an authentication token. This was done in order to support single-page applications.
It looks like your application will need to be able to fetch user information on an arbitrary user. In that case, using an Okta API token makes sense.
However, if you were making that call from a single-page application, you would want to make a request to the /users/me API endpoint.

Related

MSAL and OAuth 2.0 - Request an authorization code programmatically

Goal is to get access token from MSAL programmatically for Cypress e2e tests.
We use V2.0 API.
According to this I first need to get the authorization code: https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow#request-an-authorization-code
to get the access token https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow#request-an-access-token
So in order to get authorization code I would need to do this request
// GET
// Line breaks for legibility only
https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize?
client_id=6731de76-14a6-49ae-97bc-6eba6914391e
&response_type=code
&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F
&response_mode=query
&scope=openid%20offline_access%20https%3A%2F%2Fgraph.microsoft.com%2Fmail.read
&state=12345
&code_challenge=YTFjNjI1OWYzMzA3MTI4ZDY2Njg5M2RkNmVjNDE5YmEyZGRhOGYyM2IzNjdmZWFhMTQ1ODg3NDcxY2Nl
&code_challenge_method=S256
But this returns text/html so I would need to manually login to get the code.
Is there any way to progammatically to get the authorization code?
This is how I got it solved by creating a login command. The command fetches the token programatically and stores it into localStorage.
import 'cypress-localstorage-commands';
Cypress.Commands.add('login', () => {
const request = {
method: 'POST',
form: true,
url: `https://login.microsoftonline.com/${Cypress.config('tenantId')}/oauth2/v2.0/token`,
body: {
grant_type: 'client_credentials',
client_id: Cypress.config('clientId'),
client_secret: Cypress.config('clientSecret'),
scope: `${Cypress.config('clientId')}/.default`,
},
};
cy.request(request).then(response => cy.setLocalStorage('msal.idtoken', response.body.access_token));
});

How to authenticate inside C# Web API with Google Authentication?

I am developing a C# Web Api (.NET Framework) and would like to use in parallel the AAD authentication (already working correctly) and Google Authentication. This means, my clients (javascript or just Postman) should fetch the token, include it in the Authorization header (Bearer token) and be able to execute the API methods.
However, it seems that the token I am generating with Postman is not accepted by my C# Web Api. I am always getting a HTTP 401. The AAD (Windows Azure Active Directory) works seamlessly.
Using a C# Web Api (.Net Framework 4.6.1), including Microsoft.Owin.Security.Google Nuget package v.4.0.1.
I have created the Oauth client on the google developer console, got my client ID and client secret. Also I have set up the redirect URI there.
I am using Postman Oauth2 authorization, setting following parameters:
Grant type: implicit
Callback URL: my url
AUth URL: https://accounts.google.com/o/oauth2/v2/auth
Client ID: 65xxxxxmyclientid.apps.googleusercontent.com
scope: openid email profile
Then I can log in with my google account, give consent to use the scopes, and am getting the token like this:
Access Token
ya29.Gls7....
This token is then sent as Authorization header like "Bearer ya29.Gls7...."
Startup.Auth
public void ConfigureAuth(IAppBuilder app)
{
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
TokenValidationParameters = new TokenValidationParameters
{
ValidAudience = ConfigurationManager.AppSettings["ida:Audience"]
},
});
app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
{
ClientId = ConfigurationManager.AppSettings["GoogleClientId"],
ClientSecret = ConfigurationManager.AppSettings["GoogleClientSecret"],
});
}
ValuesController
public class ValuesController : ApiController
{
[Authorize]
// GET api/values/5
public string Get(int id)
{
return "value";
}
}
Every time I call the API using Postman, I am getting a 401, even though the bearer token is included in the request authorization header.
HTTP GET https://localhost:44385/api/values/1
Edit: There is no custom code from my side which would validate the token. I assumed that is being done automatically by the library. In fact, this is done automatically for AAD tokens, but not in the case of Google Oauth.
I expect to get inside the code of the controller method, having at least the identity name set with the google account name/email.
What am I missing to be authenticated inside my controller method with google account?

Ruby rest-client 401 Unauthorized after post of data

I'm new to using the rest-client. I know I'm missing something, but I am trying to do the following:
Post to a login endpoint to authenticate
After authentication, post csv text to another endpoint that requires a logged in user
The authentication portion is successful, however, I am getting a 401 Unauthorized when step 2 occurs.
rest_client = RestClient
login_response = #global_rest_client.post(
host + 'LOGIN ENDPOINT',
{ userName: 'user', password: 'password'},
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
)
import_response = rest_client.post(
host + 'IMPORT DATA ENDPOINT',
headers: { 'X-System-Name': 'AndroidMobile', 'Content-Type': 'multipart/form-data },
csv: csv_string
)
My understanding of how authentication works could be wrong. My assumption is that as long as the same instance of the client has a successful login, then the post of csv data would also be successful.
I appreciate any input.
HTTP (1.1) is stateless so a request does not contain any information about previous requests unless that information is encoded and added to the request in some way (e.g. cookies or headers). So when you make your import request the server does not know if/that you are authenticated even though you just made a login request.
You'll have to include the token you receive from your login request in subsequent requests. This should go in the 'Authorization' header.
For example:
auth_token = login_response["success"]["token"] # or whatever the key is for the token
import_response = rest_client.post(
host + 'IMPORT DATA ENDPOINT',
headers: { 'Authorization': "Bearer #{auth_token}", 'X-System-Name': 'AndroidMobile', 'Content-Type': 'multipart/form-data },
csv: csv_string
)
The way authentication works depends on the server and can be different in different cases. So the site you are accessing might expect the Authorization header to be like "Token #{auth_token}" or anything else, but they should mention it in their documentation.

Keycloak logout endpoint not deleting session

Hello fellow programmes,
I am stuck on the issue with keycloak. I am trying to send from node.js express framework request towards keycloak to logout the user.
Config.keycloakClient = my_realm
Config.keycloakURL = keycloak URL
request.get({
//url: join(Config.keycloakURL, '/auth/realms/'+ Config.keycloakClient+ '/protocol/openid-connect/logout?' + 'id_token_hint='+req.headers.oidc_access_token), <--- tried this
url: join(Config.keycloakURL, '/auth/realms/'+ Config.keycloakClient+ '/protocol/openid-connect/logout'), // <-- i also tried this
headers:
{ Authorization: "Bearer " + req.headers.oidc_access_token, // <-- also tried Authorization: req.headers.oidc_access_token }
Result - 200 OK, but i can still see active session in active sessions in admin interface
request.post({
//url: join(Config.keycloakURL, '/auth/realms/'+ Config.keycloakClient+ '/protocol/openid-connect/logout?' + 'id_token_hint='+req.headers.oidc_access_token), <--- tried this
url: join(Config.keycloakURL, '/auth/realms/'+ Config.keycloakClient+ '/protocol/openid-connect/logout'), // <-- i also tried this
headers:
{ Authorization: "Bearer " + req.headers.oidc_access_token, // <-- also tried Authorization: req.headers.oidc_access_token }
Result - 302 redirect, but i can still see active session in active sessions in admin interface
I was trying to find the refresh token, but when accessing
Config.keycloakURL/auth/realms/{realm}
i could not get the refesh token-> it redirects me to the login page.
In session storage / cookies i can not see anything strange via chrome dev tools.
So what is the proper way to logout with endpoint? Which endpoint and what parameters should i use please? And how am i to obtain refresh token?
Thanks for the help!
Best regards

Google OAuth: can't get refresh token with authorization code

I am using Google API Client for Google Analytics with OAuth 2.0
I read this to get the refresh token but it doesn't appears: https://developers.google.com/identity/protocols/OAuth2WebServer#offline
I only get this instead:
{
"access_token": "ya29.twHFi4LsiF-AITwzCpupMmie-fljyTIzD9lG8y_OYUdEGKSDL7vD8LPKIqdzRwvoQAWd",
"token_type": "Bearer",
"expires_in": 3599,
"id_token": "very long string"
}
Here is the code:
Javascript (to get the Authorization Code): that works
gapi.analytics.ready(function() {
gapi.analytics.auth.authorize({
container: 'embed-api-auth-container',
clientid: '257497260674-ji56vq885ohe9cdr1j6u0bcpr6hu7rde.apps.googleusercontent.com',
});
gapi.analytics.auth.on('success', function(response) {
var code = response.code;
$.ajax({
url: "getTokensFromCode.php",
method: "GET",
data: {
"code": code
},
success: function (tokens) {
// I can't get refresh token here, only get "access_token" and "id_token"
console.log(tokens);
}
});
});
});
PHP (to exchange Authorization Code for tokens): that doesn't work
// I get the authorization code in Javascript
$code = $_GET['code'];
$redirectURI = "postmessage";
$client = new Google_Client();
$client->setClientId($clientID);
$client->setClientSecret($clientSecret);
$client->setRedirectUri($redirectURI);
$client->addScope(Google_Service_Analytics::ANALYTICS_READONLY);
$client->setAccessType('offline');
$client->setApprovalPrompt('force');
$client->authenticate($code);
$tokens = $client->getAccessToken();
echo $tokens;
I need the refresh token in Javascript, that's why I get the authorization code in Javascript and make an Ajax request to get the refresh token.
You will only get the refresh_token the very first time that a user grants access to your app. You'll need to store the refresh_token somewhere to be able to use it afterwards. You won't get a new refresh token the next time a user logs in to your app.
FWIW: using a refresh token in a Javascript client doesn't make a lot of sense since a Javascript client can't store it in a safe (confidential) way. Since Javascript clients live in browsers and users are present in browsers, you can just redirect the browser to the authorization endpoint again and you'll get a new access token (which is also the purpose of a refresh token). The SSO session for the user at Google will make sure that the user doesn't need to login again and that the experience is seamless.
Check out this response by #curious_coder
Google API Refresh Token
He explains a way to get the Refresh Token every time :) Life saver. This post helped me get there so I figured I'd share this for any one else trying to find their refresh token. Add the last two lines of this code to your Auth process
$client = new Google_Client();
$client->setAuthConfigFile(__DIR__ . '/client_secrets.json');
$client->setRedirectUri('http://' . $_SERVER['HTTP_HOST'] . '/dashboard/oauthcallbacks');
$client->addScope(Google_Service_Analytics::ANALYTICS_READONLY);
$client->setAccessType('offline'); //To fetch refresh token(Refresh token issued only first time)
$client->setApprovalPrompt('force'); //Adding this will force for refresh token
Also I used var_dump($tokens) to get the contents instead of echo. Dont remember if it makes a difference in PHP but $tokens will be an array of objects.

Resources