Apache superset with Okta integration - authentication

https://github.com/apache/superset/issues/13948
I am configuring Okta with Apache Superset but it's redirecting me to the login page after authentication with message 'invalid login. Please try again.'
Below is my superset_config.py file:
AUTH_TYPE = AUTH_OAUTH
OAUTH_PROVIDERS = [
{
'name': 'okta', 'icon': 'fa-circle-o',
'token_key': 'access_token',
'remote_app': {
'client_id': '0oa8hoe9t1c8LfB1z357',
'client_secret': 'b8exxJID0BQOXlvMlQa5To5frU4EY7FX3cXDOMLM',
'api_base_url': 'https://dev-514411.okta.com/oauth2/v1/',
'client_kwargs': {
'scope': 'openid profile email groups'
},
'access_token_url': 'https://dev-514411.okta.com/oauth2/v1/token',
'authorize_url': 'https://dev-514411.okta.com/oauth2/v1/authorize'
}
}
]

Okta integration was supposed to work out of the box since Flask-AppBuilder 3.2.2, but it's not the case.
Here's what worked for me:
On your Okta's app settings, the field Sign-in redirect URIs should look something like this:
http://localhost:8088/oauth-authorized/okta
Your superset_config.py should contain something similar to this:
OKTA_BASE_URL = 'https://dev-<your-okta-id>.okta.com'
AUTH_TYPE = AUTH_OAUTH
OAUTH_PROVIDERS = [
{
'name': 'okta',
'token_key': 'access_token',
'icon': 'fa-circle-o',
'remote_app': {
'client_id': OKTA_CLIENT_ID,
'client_secret': OKTA_CLIENT_SECRET,
'client_kwargs': {
'scope': 'openid profile email groups'
},
'access_token_method': 'POST',
'api_base_url': f'{OKTA_BASE_URL}/oauth2/v1/',
'access_token_url': f'{OKTA_BASE_URL}/oauth2/v1/token',
'authorize_url': f'{OKTA_BASE_URL}/oauth2/v1/authorize',
'server_metadata_url': f'{OKTA_BASE_URL}/.well-known/openid-configuration',
},
}
]
from custom_sso_security_manager import CustomSsoSecurityManager
CUSTOM_SECURITY_MANAGER = CustomSsoSecurityManager
And finally, your custom_sso_security_manager.py, that must live on the same directory as your superset_config.py, should contain something like this:
from superset.security import SupersetSecurityManager
class CustomSsoSecurityManager(SupersetSecurityManager):
def oauth_user_info(self, provider, response=None):
if provider == 'okta':
user_info = self.appbuilder.sm.oauth_remotes[provider].parse_id_token(
response)
return {
'name': user_info['name'],
'email': user_info['email'],
'id': user_info['email'],
'username': user_info['email']
}
The important attributes on the object oauth_user_info returns are email and username, which will be used to match against your database's ab_user table records. If there isn't a matching record then the login will fail.

Related

How can i fix this error : { "detail": "Authentication credentials were not provided." } on Django RestFramework

I am getting
{"detail": "Authentication credentials were not provided." }
when I run Django Restframework code.
in my settings.py
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 10,
'NON_FIELD_ERRORS_KEY': 'error',
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
),
Here is where the culprit lies when i make a request to input values
class CashflowListAPIView(ListCreateAPIView):
serializer_class = CashflowSerializer
queryset = Cashflow.objects.all()
permission_classes = (permissions.IsAuthenticated,)
def perform_create(self,serializer):
return serializer.save(owner=self.request.user)
def get_queryset(self):
return self.queryset.filter(owner = self.request.user)
class CashflowDetailAPIView(RetrieveUpdateDestroyAPIView):
serializer_class = CashflowSerializer
permission_classes = (permissions.IsAuthenticated,IsOwner,)
queryset = Cashflow.objects.all()
lookup_fields = 'id'
def get_queryset(self):
return self.queryset.filter(owner=self.request.user)
mind you, its supposed to work since user has gone through the authentication process VIA swagger API system on localhost:
SWAGGER_SETTINGS = {
'SECURITY_DEFINITIONS': {
'Bearer': {
'type': 'apiKey',
'name': 'Authorization',
'in': 'header'
}
}
}
How can i get my user to successfully input and fetch data without getting the message, { "detail": "Authentication credentials were not provided." }?
Your input is valued and appreciated. Thanks

How to create a follow/followed relationship using Neo4j and Neo4j GraphQL Library with #auth?

Using the following schema:
type Profile {
id: ID! #id
username: String! #unique(constraintName: "unique_email")
follows: [Profile!]!
#relationship(
type: "FOLLOWS"
properties: "BaseRelationship"
direction: OUT
)
followers: [Profile!]!
#relationship(
type: "FOLLOWS"
properties: "BaseRelationship"
direction: IN
)
}
And extending it to use the #auth directive:
extend type Profile
#auth(
rules: [
{ operations: [CREATE, UPDATE, DELETE, UPDATE, CONNECT, DISCONNECT], allow: { username: "$jwt.name" } }
{ operations: [READ], isAuthenticated: true }
]
)
I am only getting forbidden when I try the following mutation (jwt.name is user1):
mutation UpdateProfiles {
updateProfiles(
where: { username: "user1" }
update: {
follows: [{ connect: [{ where: { node: { username: "user2" } } }] }]
}
) {
info {
relationshipsCreated
}
}
}
Just want to make user1 follow user2 and vice versa. Tried changing allow to bind and to where and variations of the mutation but I can't get it to work.
If I use the same mutation but make both arguments user1, then user1 is able to follow himself, so I assume there is something wrong with not being able to allow the incoming connection to user2.
But how can I allow that the authorized user can follow someone else?

Vue js Django Rest Framework With JWT user authentication (getting anonymous user ) simplejwt simplejwt

I got the token.But it is not possible to get data this user. which URL to get data .
token work. //simplejwt simplejwt
class CustomerRetrieveView(generics.RetrieveAPIView):
queryset = Customer.objects.all()
permission_classes=(IsAuthenticated,)
def get(self,request):
???here correct?????
queryset=Customer.objects.get(id=request.user)
serializer=CustomerSerializer(queryset)
return Response(serializer.data)
url
??here correct?????
path('customers/????<int:id>???', views.CustomerRetrieveView.as_view()),
frontend
created() {
getAPI.get('???????????/customers????????', { headers: { Authorization: `Bearer ${this.$store.state.accessToken}` } })
.then(response => {
this.$store.state.APIData = response.data
})
.catch(err => {
console.log(err)
console.log(`Bearer ${this.$store.state.accessToken}`)
})
},
models
class Customer(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
phone = models.CharField(max_length=11)
likecus=models.ManyToManyField(smartphone ,verbose_name="b")
def __str__(self):
return "User: {}, phone: {}".format(self.user, self.phone)
Serializer
class CustomerSerializer(serializers.ModelSerializer):
class Meta:
model = Customer
fields = '__all__'
I got the token.But it is not possible to get data this user. which URL to get data .
token workI got the token.But it is not possible to get data this user. which URL to get data .
token workI got the token.But it is not possible to get data this user. which URL to get data .
token work
you can do this
pip install djangorestframework-jwt
settings.py
from django.conf import settings
import datetime
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
),
}
JWT_AUTH = {
'JWT_ENCODE_HANDLER':
'rest_framework_jwt.utils.jwt_encode_handler',
'JWT_DECODE_HANDLER':
'rest_framework_jwt.utils.jwt_decode_handler',
'JWT_PAYLOAD_HANDLER':
'rest_framework_jwt.utils.jwt_payload_handler',
'JWT_PAYLOAD_GET_USER_ID_HANDLER':
'rest_framework_jwt.utils.jwt_get_user_id_from_payload_handler',
'JWT_RESPONSE_PAYLOAD_HANDLER':
'rest_framework_jwt.utils.jwt_response_payload_handler',
'JWT_SECRET_KEY': settings.SECRET_KEY,
'JWT_GET_USER_SECRET_KEY': None,
'JWT_PUBLIC_KEY': None,
'JWT_PRIVATE_KEY': None,
'JWT_ALGORITHM': 'HS256',
'JWT_VERIFY': True,
'JWT_VERIFY_EXPIRATION': True,
'JWT_LEEWAY': 0,
'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=300),
'JWT_AUDIENCE': None,
'JWT_ISSUER': None,
'JWT_ALLOW_REFRESH': False,
'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=7),
'JWT_AUTH_HEADER_PREFIX': 'JWT',
'JWT_AUTH_COOKIE': None,
}
urls.py
from rest_framework_jwt.views import obtain_jwt_token, refresh_jwt_token, verify_jwt_token
urlpatterns = [
path(r'^api-token-refresh/', refresh_jwt_token),
path(r'^api-token-auth/', obtain_jwt_token),
path(r'^api-token-verify/', verify_jwt_token),
]
curl -X POST -d "username=admin&password=password123" http://localhost:8000/api-token-auth/

About the implementation of Remember Me using AuthenticationPlugin's Cookie Authenticator

I use CakePHP's AuthenticationPlugin. I was trying to implement RememberMe functionality into this.
I found the following article when I was reading the Cakephp documentation.
Cookie Authenticator aka “Remember Me”
However, the documentation here is difficult for me to understand. I have no idea what to do with it.
I've successfully implemented EncryptedCookieMiddleware. I have no idea what to do after that.
I don't know how to use rememberMeField, how to use fields and how to use cookies.
$this->Authentication->rememberMeField
$this->Authentication->fields
I tried to see if I could use it like these, but it was still no good.
Please let me know how to use these.
Also, do you know of any RememberMe tutorials?
How do I implement it?
Sorry. Please help me...
// in config/app.php
'Security' => [
.....
'cookieKey' => env('SECURITY_COOKIE_KEY', 'AnyString'), // <- add
],
// in src/Application.php
use Cake\Http\Middleware\EncryptedCookieMiddleware; // <- add
// in middleware()
public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue
{
$cookies = new EncryptedCookieMiddleware( // <- add
['mail', 'password'],
Configure::read('Security.cookieKey')
);
$middlewareQueue
// . . .
->add($cookies) // <-add
->add(new AuthenticationMiddleware($this));
So far I've been able to implement it myself. I'm confident.
The problem is after this. We have no idea what to do with it...
A Remember me checkbox was implemented in the template Form.
$this->request->getData('rememberMe'); to get it.
If this is 1, the checkbox was pressed.
// in src/Controller/UsersController
public function login()
{
$this->request->allowMethod(['get', 'post']);
if ($this->request->is('post')) {
$result = $this->Authentication->getResult();
// If the user is logged in, whether POST or GET, we will redirect
$requestGetData = $this->request->getData('rememberMe');
if ($requestGetData['rememberMe'] == 1){
$this->Authentication->cookie['name'] = $requestGetData['mail'];
$this->Authentication->cookie['name'] = $requestGetData['password']
}
if ($result->isValid()) {
$redirect = $this->request->getQuery('redirect', [
'controller' => 'Stores',
'action' => 'index',
]);
return $this->redirect($redirect);
}
// If the user fails to authenticate after submitting, an error is displayed.
if (!$result->isValid()) {
$this->Flash->error(__('Your email address or password is incorrect.'));
}
}
$title = $this->config('Users.title.login');
$message = $this->config('Users.message.login');
$this->set(compact('login_now', 'title', 'message'));
}
I know that's not true. But I tried to implement something like this just in case.
Please help me!
Changed around the login.
public function login()
{
$this->request->allowMethod(['get', 'post']);
if ($this->request->is('post')) {
$result = $this->Authentication->getResult();
$requestData = $this->request->getData();
if ($result->isValid()) {
$redirect = $this->request->getQuery('redirect', [
'controller' => 'Stores',
'action' => 'index',
]);
$this->Authentication->getAuthenticationService()->loadAuthenticator( 'Authentication.Cookie', [
'fields' => ['mail', 'password']
]
);
return $this->redirect($redirect);
}
if ($this->request->is('post') && !$result->isValid()) {
$this->Flash->error(__('Your email address or password is incorrect.'));
}
}
$title = $this->config('Users.title.login');
$message = $this->config('Users.message.login');
$this->set(compact('title', 'message'));
}
You're not supposed to load authenticators in your controllers, authentication happens at middleware level, before any of your controllers are being invoked.
The cookie authenticator is ment to be loaded and configured just like any other authenticator, that is where you create the authentication service, usually in Application::getAuthenticationService() in src/Application.php.
By default the field in the form must be remember_me, not rememberMe, that is unless you would configure the cookie authenticator's rememberMeField option otherwise.
Furthermore the default cookie name of the cookie authenticator is CookieAuth, so if you wanted to encrypt it, you'd have to use that name in the EncryptedCookieMiddleware config accordingly.
tl;dr
Remove all cookie related code from your controller, and load the authenticator in your Application::getAuthenticationService() method:
use Authentication\Identifier\IdentifierInterface;
// ...
public function getAuthenticationService(ServerRequestInterface $request): AuthenticationServiceInterface
{
$service = new AuthenticationService();
// ...
// The cookie authenticator should be loaded _after_ the session authenticator,
// and _before_ other authenticators like the form authenticator
$service->loadAuthenticator('Authentication.Cookie', [
// 'rememberMeField' => 'custom_form_field_name', // if you want to change the default
'fields' => [
IdentifierInterface::CREDENTIAL_USERNAME => 'mail',
IdentifierInterface::CREDENTIAL_PASSWORD => 'password',
],
]);
// ...
return $service;
}
set the authentication cookie name in the EncryptedCookieMiddleware config:
$cookies = new EncryptedCookieMiddleware(
['CookieAuth'],
Configure::read('Security.cookieKey')
);
and change the field name in your form to remember_me if you're using the cookie authenticator's defaults:
echo $this->Form->control('remember_me', [
'type' => 'checkbox'
]);
That's all that should be required, if you tick the checkbox in your login form, then the authentication middleware will set a cookie after successful authentication accordingly, and it will pick up the cookie if it's present on a request and no other authenticator successfully authenticates the request first (like the session authenticator for example).

Firebase make user object from auth data

So I'm using Angularfire in an ionic app and trying to figure out how to make a user object that is associated with the auth data from an Auth $createUser call. My first try had the auth call and the user got authenticated, then a user object was made and pushed into a $firebaseArray which works fine, but I don't know how to grab the current user after they are logged in to update, destory, or do anything with that users data. I have made it work with looping through the users array and matching the uid from the user array item and the auth.uid item which was set to be the same in the user array object creation. This seems really ineffecient to loop over if there is a large user array and it needs to be done on multiple pages.
My current attempt is using a different method like so:
angular.module('haulya.main')
.controller('RegisterController', ['Auth', '$scope', 'User', '$ionicPlatform', '$cordovaCamera','CurrentUserService',
function(Auth, $scope, User, $ionicPlatform, $cordovaCamera, CurrentUserService) {
//scope variable for controller
$scope.user = {};
console.log(User);
$scope.createUser = function(isValid) {
var userModel;
$scope.submitted = true;
//messages for successful or failed user creation
$scope.user.message = null;
$scope.user.error = null;
//if form is filled out valid
if(isValid) {
//Create user with email and password firebase Auth method
Auth.$createUser({
email: $scope.user.email,
password: $scope.user.password
})
.then(function(userData) {
userModel = {
uid: userData.uid,
photo: $scope.user.photo || null,
firstName: $scope.user.firstName,
lastName: $scope.user.lastName,
email: $scope.user.email,
cell: $scope.user.cell,
dob: $scope.user.dob.toString(),
city: $scope.user.city,
state: $scope.user.state,
zip: $scope.user.zip
}
// add new user to profiles array
User.create(userModel).then(function(user) {
$scope.sharedUser = User.get(user.path.o[1]);
});
$scope.user.message = "User created for email: " + $scope.user.email;
})
.catch(function(error) {
//set error messages contextually
if(error.code == 'INVALID_EMAIL') {
$scope.user.error = "Invalid Email";
}
else if(error.code == 'EMAIL_TAKEN'){
$scope.user.error = "Email already in use, if you think this is an error contact an administrator";
}
else {
$scope.user.error = "Fill in all required fields";
}
});
}
};
//Get profile pic from camera, or photo library
$scope.getPhoto = function(type) {
$ionicPlatform.ready(function() {
//options for images quality/type/size/dimensions
var options = {
quality: 65,
destinationType: Camera.DestinationType.DATA_URL,
sourceType: Camera.PictureSourceType[type.toUpperCase()],
allowEdit: true,
encodingType: Camera.EncodingType.JPEG,
targetWidth: 100,
targetHeight: 100,
popoverOptions: CameraPopoverOptions,
saveToPhotoAlbum: false
};
//get image function using cordova-plugin-camera
$cordovaCamera.getPicture(options)
.then(function(photo) {
$scope.user.photo = photo;
}, function(err) {
console.log(err);
});
});
};
}]);
And here's the service the controller is using:
angular
.module('haulya.main')
.factory('User', function($firebaseArray) {
var ref = new Firebase('https://haulya.firebaseio.com');
var users = $firebaseArray(ref.child('profiles'));
var User = {
all: users,
create: function(user) {
return users.$add(user);
},
get: function(userId) {
return $firebaseArray(ref.child('profiles').child(userId));
},
delete: function(user) {
return users.$remove(user);
}
};
return User;
});
This also works, but again I don't have a solid reference to the currently logged in users object data from the array. The objects id is only stored on the controllers scope.
I looked through other posts, but they were all using older versions of firebase with deprecated methods.
If you're storing items that have a "natural key", it is best to store them under that key. For users this would be the uid.
So instead of storing them with $add(), store them with child().set().
create: function(user) {
var userRef = users.$ref().child(user.uid);
userRef.set(user);
return $firebaseObject(userRef);
}
You'll note that I'm using non-AngularFire methods child() and set(). AngularFire is built on top of Firebase's regular JavaScript SDK, so they interoperate nicely. The advantage of this is that you can use all the power of the Firebase JavaScript SDK and only use AngularFire for what it's best at: binding things to Angular's $scope.
Storing user data is explained in Firebase's guide for JavaScript. We store them under their uid there too instead of using push(), which is what $add() calls behind the scenes.