Can you customize Ktor 401 - Unauthorized Response? - kotlin

When implementing Basic Authentication on Ktor and configuring a Provider, which validates whether the credentials are legit by returning a non null Principal, like in this example:
install(Authentication) {
basic("auth-basic") {
realm = "Access to the '/' path"
validate { credentials ->
if (credentials.name == "fernando" && credentials.password == "foobar") {
UserIdPrincipal(credentials.name)
} else {
null
}
}
}
}
If the credentials are invalid and a null is returned, then Ktor automatically communicates with the client by triggering a 401 - Unauthorized, which in terms of behavior is what is expected...
But I cannot provide/add any extra information, like for example where exactly the issue was: username or password.
Any idea on how to mitigate this?

for resolve this problem you can use StatusPages by install it on application calss.
like below:
install(StatusPages) {
status(HttpStatusCode.Unauthorized) {
call.respond(HttpStatusCode.Unauthorized, "Your Response Object")
}
}
for more informatin please read these links:
https://ktor.io/docs/status-pages.html
https://github.com/ktorio/ktor/issues/366

The message when the token expires can be shown using StatusPages or by using the challenge method in the JWTAuth class like this:
jwt {
challenge { _, _ ->
call.respond(HttpStatusCode.Unauthorized, "Token is not valid or has expired")
}
}

Related

Getting Forbidden 403 error while creating OnlineMeeting using Microsoft Graph api

I am trying to create online meeting through https://graph.microsoft.com/beta/communications/onlineMeetings .
initially i got the 403 Forbidden error. then i give the Delegated and Application permissions(OnlineMeetings.ReadWrite,OnlineMeetings.ReadWrite.All) on my registered app on azure.then 403 error is gone and i got new error 400 bad request(Organizer.Identity.User.Id missing).
then i supply Online meeting post request as follows-:
{
"startDateTime":"2020-04-20T14:33:30.8546353-07:00",
"endDateTime":"2020-04-20T15:03:30.8566356-07:00",
"subject":"Application Token Meeting",
"participants": {
"organizer": {
"identity": {
"user": {
"id": "cb6d6636-1c9e-457c-b904-5da8265a8927"
}
}
}
}
}
again i got 403 Forbidden error.
i created user with the help of https://graph.microsoft.com/v1.0/users and https://graph.microsoft.com/beta/invitations.
user is created in my app on azure and i give the permission (User.ReadWrite.All, Directory.ReadWrite.All, Directory.AccessAsUser.All,User.ReadWrite.All, Directory.ReadWrite.All)
but error(403) did not change.
My question is that how to give user authorization in microsoft graph API.
The required permission is Delegated Permission: OnlineMeetings.ReadWrite. See reference here.
403 error means your access token doesn't include the required permission. You can add it like this:
Don't forget to click on "Grant admin consent for {your tenant}".
You should implement Get access on behalf of a user and get an access token to call /communications/onlineMeetings endpoint.
The http sample for your reference:
POST https://graph.microsoft.com/beta/communications/onlineMeetings
Content-Type: application/json
Authorization: Bearer {access token}
{
"startDateTime":"2019-09-09T14:33:30.8546353-07:00",
"endDateTime":"2019-09-09T15:03:30.8566356-07:00",
"subject":"Application Token Meeting",
"participants": {
"organizer": {
"identity": {
"user": {
"id": "550fae72-d251-43ec-868c-373732c2704f"
}
}
}
}
}
You can learn more details about Use the access token to call Microsoft Graph. Don't forget to put Authorization: Bearer {access token} in the request headers.

JitBit Helpdesk API

Wondering if anyone else is using the JitBit Helpdesk software and their API
https://www.jitbit.com/helpdesk/helpdesk-api/
We are unable to authenticate to the interface as it returns a 401 every time. I'm not sure if we are setting the headers incorrectly or if we need to enable something in the ticketing software to allow us access.
https://www.programmableweb.com/api/jitbit-helpdesk/sample-source-code
We've tried connecting through node and python and receive the same results. Here is an example of what i'm using through node
const options = {
url: 'https://helpdesk/api/Authorization',
rejectUnauthorized: false,
headers: {
Authorization: "Basic " + Buffer.from('Domain\\username' + ":" + 'password').toString('base64')
}
}
function callback(error, response, body) {
console.log(response)
if (!error && response.statusCode == 200) {
const info = JSON.parse(body);
console.log('success',info)
}
}
request(options,callback)
I had to set the rejectUnauthorized to false, it was saying it couldn't return the certificate (UNABLE_TO_GET_ISSUER_CERT_LOCALLY). Not sure if that could be the issue.
Any help would be greatly appreciated.
Got it resolved. We had anonymous access disabled. It was recommend by support to have this enabled otherwise you'll run into issues such as authenticating to the API.

Redirect_URI error when using GoogleAuth.grantOfflineAccess to authenticate on server

I'm trying to use the authorization flow outlined at https://developers.google.com/identity/sign-in/web/server-side-flow.
I've created the credentials as indicated... with no Authorized redirect URIs specified as the doc indicates: "The Authorized redirect URI field does not require a value. Redirect URIs are not used with JavaScript APIs."
The code initiating the authorization is:
Client button and callback:
<script>
$('#signinButton').click(function() {
var auth2 = gapi.auth2.getAuthInstance();
auth2.grantOfflineAccess().then(signInCallback);
});
function signInCallback(authResult) {
console.log('sending to server');
if (authResult['code']) {
// Send the code to the server
$.ajax({
type: 'POST',
url: 'CheckAuth',
headers: {
'X-Requested-With': 'XMLHttpRequest'
},
contentType: 'application/octet-stream; charset=utf-8',
success: function(result) {
// Handle or verify the server response.
},
processData: false,
data: authResult['code']
});
} else {
// There was an error.
}
}
</script>
Server side (CheckAuth method to create credentials from auth code, which it receives correctly via the javascript callback):
private Credential authorize() throws Exception {
// load client secrets
InputStream is = new FileInputStream(clientSecretsPath_);
InputStreamReader isr = new InputStreamReader(is);
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, isr);
String redirect_URI = "";
GoogleTokenResponse tokenResponse =
new GoogleAuthorizationCodeTokenRequest(
httpTransport, JSON_FACTORY,
"https://www.googleapis.com/oauth2/v4/token",
clientSecrets.getDetails().getClientId(),
clientSecrets.getDetails().getClientSecret(),
token_,
redirect_URI)
.execute();
String accessToken = tokenResponse.getAccessToken();
// Use access token to call API
GoogleCredential credential = new GoogleCredential().setAccessToken(accessToken);
return credential;
}
The flow works correctly, up until the point my server attempts to exchange the authorization code for the token response (GoogleAuthorizationCodeTokenRequest.execute() )... the auth server returns:
400 Bad Request
{
"error" : "invalid_request",
"error_description" : "Missing parameter: redirect_uri"
}
Given the error, I looked in debug at the auth instance in javascript and noted what it indicated was the redirect_uri. I then updated my google credentials and specified that URI in the Authorized redirect URIs (it's the URL that accessed the javascript, as the auth server correctly returns to the specified javascript callback). With the updated credentials and the URI specified in the instantiation of GoogleAuthorizationCodeTokenRequest (String redirect_URI = "http://example.com:8080/JavascriptLocation";), the error then becomes:
400 Bad Request
{
"error" : "redirect_uri_mismatch",
"error_description" : "Bad Request"
}
I've tracked all the way through to the actual HttpRequest to the auth server (www.googleapis.com/oauth2/v4/token) and cannot tell what redirect_uri it is looking for.
Does anyone know what the value of redirect_uri should be in this case (when using grantOfflineAccess())? I'm happy to post more of the code, if that is at all helpful... just didn't want to flood the page. Thanks.
Found a reference to "postmessage" right after posting the question... using it as the redirect_URI on the server side seems to generate a successful response from the auth server. So... setting redirect_URI="postmessage" in the code below appears to work in this situation.
GoogleTokenResponse tokenResponse =
new GoogleAuthorizationCodeTokenRequest(
httpTransport, JSON_FACTORY,
"https://www.googleapis.com/oauth2/v4/token",
clientSecrets.getDetails().getClientId(),
clientSecrets.getDetails().getClientSecret(),
token_,
redirect_URI)
.execute();

Meteor Accounts obtaining the SAML Response (Token / User after log in)

i have a workin SAML logon with Meteor.accounts and MS Azure. I am using this https://github.com/steffow/meteor-accounts-saml library for SAML which is derived from https://github.com/bergie/passport-saml
The procedure goes like this:
Click saml login -> popup appears
Enter user/pw data in popup -> popup closes without error
Logged in success
So now i want to get the SAML Token for further processing (or at least the information about the logged in user which meteor has taken from the IDP).
Since i dont have a clue where the SAML Token is stored by Meteor or can be fetched in the code, can someone help me getting the SAML Response?
Probably solved by now but here it goes..
According to the code the saml response is retrieved in "saml_server.js" at row 70 and the token should be in there also (loginRequest.credentialToken)
loginResult should be fairly easy to save into the Meteor.users collection
var loginResult = Accounts.saml.retrieveCredential(loginRequest.credentialToken);
var email = loginResult.profile.email;
var user = Meteor.users.findOne({username: email});
if (!user) {
Accounts.createUser({
"username": email,
"profile": StufffromloginResult
});
} else {
Meteor.users.update(
{
"username": email
},
{ "$set": {
"profile": StufffromloginResult,
}
});
}
And retrieved with:
Meteor.user().profile

Akka authenticateOAuth2Async: credentials missing

I have the following:
def myUserPassAuthenticator(credentials: Credentials): Future[Option[String]] = {
log.info(credentials.toString)
credentials match {
case p#Credentials.Provided(id) if p.verify("a") =>
log.info("Login success!")
Future.successful(Some(id))
case _ =>
log.info("Login failure!")
Future.successful(None)
}
}
val authRoute = path("login") {
authenticateOAuth2Async(realm = "secure site", myUserPassAuthenticator) { userName =>
complete(s"The user is '$userName'")
}
}
when navigating to that endpoint and entering credentials, the log line
log.info(credentials.toString)
just becomes Missing. What is wrong here?
The content-type of the request is "application/x-www-form-urlencoded"
and the data is "grant_type=password&username=INSERT_USERNAME_HERE&password=INSERT_PWD_HERE"
You should wrap your route in Route.seal directive:
val authRoute =
Route.seal {
path("login") {
authenticateOAuth2Async(realm = "secure site", myUserPassAuthenticator) { userName =>
complete(s"The user is '$userName'")
}
}
}
from documentation it seems to rely on default rejection 401 if authentication fails
Given a function returning Some[T] upon successful authentication and
None otherwise, respectively applies the inner route or rejects the
request with a AuthenticationFailedRejection rejection, which by
default is mapped to an 401 Unauthorized response.
and the Route.seal provides exactly that:
A Route can be "sealed" using Route.seal, which relies on the in-scope
RejectionHandler and ExceptionHandler instances to convert rejections
and exceptions into appropriate HTTP responses for the client.