Symfony2 - HWI/OAuthBundle can't find user provider - facebook

Trying to connect facebook using HWI/OAuthBundle, following all of the steps that say in HWI/OauthBundle Doc, but it return error
There is no user provider for user "HWI\Bundle\OAuthBundle\Security\Core\User\OAuthUser"
Does anyone can explain for me why this happen and how to fix this problem?

Add to security.yml
providers:
hwi:
id: hwi_oauth.user.provider
or your custom user provider service

Above is almost correct. It should be added to security.yml not services.yml. See below for example in context.
providers:
in_memory:
memory:
users:
admin: { password: somesecretpassowrd, roles: 'ROLE_ADMIN' }
hwi:
id: hwi_oauth.user.provider

This exception appears when services.yml is put below security.yml in config.yml. Apparently, hwi_oauth service has to be defined before it's going to be used in security providers. So the correct sequence of resources in config.yml is supposed to be:
- { resource: services.yml }
- { resource: security.yml }

Related

Serverless - Referencing api gateway address in CloudFront origin definition

Using Serverless Framework,
i am creating an api gateway in my template :
functions
test:
handler: test.handler
events:
- http:
path: save-subscription
method: post
cors: false
later on i want to use this api address xxxxxxx.execute-api.us-east-1.amazonaws.com/dev/
as a domain name of an oigin of my cloudfront origins
DomainName: xxxxxxx.execute-api.us-east-1.amazonaws.com
OriginPath: dev
Is there a way to reference the api address dynamically/programmatically before it even exist, and do I need to use depends on?
Thanks
- DomainName:
Fn::Join:
- ""
- - "Ref": "ApiGatewayRestApi"
- ".execute-api.${self:custom.region}.amazonaws.com"
worked for me.
Found the answer here : https://www.richdevelops.dev/blog/how-do-i-get-my-api-gateway-url

Restrict API access to specific app clients

I have built an iOS and an Android app. These apps use Cognito User Pools to allow public authenticated and unauthenticated access to API Gateway.
I am trying to prevent third-party apps from accessing this API. I only want apps that I have authorized to have API access. Is possible to limit access to APIG to only my apps?
Cognito provides the notion of an App Client ID and App Client Secret in the settings for the user pool. Is this the preferred mechanism to limit Cognito logins to whitelisted clients/apps? The docs provide little clarification as to the purpose of this configuration or a best practice for keeping these keys secure in the wild.
Are there alternative methods to achieve what I'm attempting? Is this goal even possible to accomplish? I believe an app could be reverse engineered for these keys or they could be discovered via a network sniffer.
I am a bit of a novice on API security so your insights are appreciated.
Have a look at Cognito Identity. Cognito Federated Identity Pools provide for both authenticated & unauthenticated access. This is going to be long, so please bear with me. (my code examples are using cloudformation in yaml or JS). I will assume that you have created your user pool & app client. You are going to need those to create an identity pool. I will also assume that your user pool allowed oauth flows is set to implicit grant and allowed oauth scope is openid. This is required to get the id_token which is used to create a federated identity.
create cognito identity pool using your cognito user pool as an authenticated provider. Sample CFN yaml for the same
AccIdenAdminPool:
Type: "AWS::Cognito::IdentityPool"
Properties:
IdentityPoolName: <identity pool name as input>
AllowUnauthenticatedIdentities: true
CognitoIdentityProviders:
- ClientId: <your app client id>
ProviderName: "cognito-idp.us-east-1.amazonaws.com/<your user pool id>"
ServerSideTokenCheck: true
Now attach your authenticated & unauthenticated roles to the identity pool you just created. Sample JS code -
module.exports.attachRole = (event, context, callback) => {
console.log(JSON.stringify(event)); // successful response
let params = {
IdentityPoolId: event.identityPoolId, /* required */
Roles: {
/* required */
'authenticated': <auth role arn>,
'unauthenticated': <unauth role arn>
},
};
cognitoidentity.setIdentityPoolRoles(params, function (err, data) {
if (err) {
console.log(err, err.stack);
}
else {
console.log("success"); // successful response
}
});
}
Change API gateway authentication from Cognito user pool authorizer to AWS_IAM. This is a must. If for some reason you cannot do this, you need to figure some other way out to have closed unauthenticated access to you APIs.
For authenticated access, use the id_token (received after successful login), identity pool id & user pool id to get CognitoIdentityCredentials. Sample code -
function getAccessToken(idToken, idenPoolId, userPool) {
let region = idenPoolId.split(":")[0];
let provider = "cognito-idp." + region + ".amazonaws.com/" + userPool;
let login = {};
login[provider] = idToken;
console.log(provider + ' || ' + idenPoolId);
// Add the User's Id Token to the Cognito credentials login map.
let credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: idenPoolId,
Logins: login
});
//call refresh method in order to authenticate user and get new temp credentials
credentials.get((error) => {
if (error) {
console.error(error);
} else {
console.log('Successfully logged!');
console.log('AKI:'+ credentials.accessKeyId);
console.log('AKS:'+ credentials.secretAccessKey);
console.log('token:' + credentials.sessionToken);
}
});
}
Use this access key,secret keyandtoken` to hit your APIs. It will have permissions based on your authenticated role you provisioned in step 2.
For unauthenticated access, login step will obviously be skipped but you can still generate temporary keys for access to your APIs. Sample code is very similar, with one key difference. Logins parameter is not required.
function getUnauthToken(idenPoolId) {
console.log(idenPoolId);
// Add the User's Id Token to the Cognito credentials login map.
let credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: idenPoolId,
});
credentials.get((error) => {
if (error) {
console.error(error);
} else {
console.log('Unauth AKI:'+ credentials.accessKeyId);
console.log('Unauth AKS:'+ credentials.secretAccessKey);
console.log('Unauth token:' + credentials.sessionToken);
}
});
}
This set of keys has permissions based on your unauthenticated role as provisioned in step 2.
Roles - this is how created roles & it's policy for my API gateway. Example in CFN yaml
AuthenticatedRole:
Type: "AWS::IAM::Role"
Properties:
RoleName: "AuthenticatedRole"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Action:
- "sts:AssumeRoleWithWebIdentity"
Principal:
Federated:
- "cognito-identity.amazonaws.com"
Condition:
StringEquals:
cognito-identity.amazonaws.com:aud: <your identity pool id>
ForAnyValue:StringLike:
cognito-identity.amazonaws.com:amr: authenticated
Path: "/"
AuthRolePolicy:
Type: "AWS::IAM::Policy"
Properties:
PolicyName: AuthRolePolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Action: "execute-api:Invoke"
Resource:
- "arn:aws:execute-api:<region>:<account id>:<api id>/*/*/acc/*"]]
Roles:
-
Ref: AuthenticatedRole
UnauthRole:
Type: "AWS::IAM::Role"
Properties:
RoleName: UnauthRole
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Action:
- "sts:AssumeRoleWithWebIdentity"
Principal:
Federated:
- "cognito-identity.amazonaws.com"
Condition:
StringEquals:
cognito-identity.amazonaws.com:aud: <your identity pool id>
Path: "/"
UnauthRolePolicy:
Type: "AWS::IAM::Policy"
Properties:
PolicyName: UnauthRolePolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Action: "execute-api:Invoke"
Resource:
- "arn:aws:execute-api:<region>:<account id>:<api id>/*/GET"/acc/dept/12/*"]]
Roles:
-
Ref: UnauthRole
So based on above roles I have different access for my authenticated & unauthenticated users. Only caveat here is that your identity pool id must be a secret (i.e. not a good thing to expose in the browser).
Hope this helps.

API-Platform JWT : No route found for "GET /api/login"

I successfully installed API Platform, it works well with all my entities.
Now i'm trying to add JWT authentication whith LexikJWTAuthenticationBundle, but when i send the request for login i get :
No route found for "GET /api/login"
My request :
http://localhost:8000/api/login?username=john&password=doe
I'm using Symfony 4, here is my security.yaml :
encoders:
App\Entity\User:
algorithm: bcrypt
providers:
entity_provider:
entity:
class: App\Entity\User
property: username
firewalls:
login:
pattern: ^/api/login
stateless: true
anonymous: true
provider: entity_provider
json_login:
check_path: /api/login
username_path: email
password_path: password
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
pattern: ^/
provider: entity_provider
stateless: true
anonymous: true
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
access_control:
- { path: ^/api/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
I think the JWT bundle works well because when i try to access a resource, i get :
{"code":401,"message":"JWT Token not found"}
I think it's just a matter of routing, but as i'm quite a newbie to Symfony i don't know what to do...
I already tried to change patterns, check path...
Any hint ?
EDIT : i added this in routes.yaml :
api_login_check:
path: /api/login
Now i have :
Unable to find the controller for path "/api/login". The route is wrongly configured.
More details from the logs :
WARNING
09:40:52
request Unable to look for the controller as the "_controller" parameter is missing.
ERROR
09:40:53
request Uncaught PHP Exception Symfony\Component\HttpKernel\Exception\NotFoundHttpException: "Unable to find the controller for path "/api/login". The route is wrongly configured."
Check this link https://github.com/symfony/symfony-docs/pull/7081/files#diff-7f5c7908922a550bda01ab86f19f3938R119
You have to send your request to http://localhost:8000/api/login with a json body like this
{"username": "john","password": "doe"}
Also I recommend to read this "How to Build a JSON Authentication Endpoint" https://symfony.com/doc/current/security/json_login_setup.html
Thanks
Thanks lvillarino, i already tried that without success but i think i made other mistake then... I tried again as i was quite sure that was the good direction and now it works !
This is my final configuration, maybe it will help someone...
FYI, i'm using email/password as credentials.
security.yaml
providers:
entity_provider:
entity:
class: App\Entity\User
property: email
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
login:
pattern: ^/api/login
stateless: true
anonymous: true
provider: entity_provider
json_login:
check_path: /api/login
username_path: email
password_path: password
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
main:
pattern: ^/
provider: entity_provider
stateless: true
anonymous: true
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
encoders:
App\Entity\User:
algorithm: bcrypt
access_control:
- { path: ^/api/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
routes.yaml
api_login_check:
path: /api/login
I tried to login to my api using Postman using this url :
http://localhost:8000/api/login
On the headers section i have :
Accept : application/json
Content-type: application/json
And on the body section (raw) :
{
"email":"john#doe.fr",
"password":"mypass"
}
The token is successfully generated and functional, it is accepted as Bearer in further api calls.
More information : i had problems with private and public keys generated using PuttyGen on windows, i always had bad credentials when using the generated token. I had to generate them on a linux environment using openssl, and now it works.
Last information : i installed LexikJWTAuthenticationBundle with composer, i used the passphrase generated in .env file to generate the keys, as it's not very well explained in the installation process.

HWIOAuthBundle facebook 443 Bad Access

I'm building a symfony REST Api in which I'm trying to get HWIOAuthBundle, FOSUserBundle and LexikJWTBundle working all together.
I followed this gist for the HWIOAuthBundle/FOSUserBundle integration.
Now I'm getting the facebook login form when hitting the /login route. But after submition I get this error :
[2/2] HttpTransportException: Error while sending HTTP request
[1/2] RequestException: Failed to connect to graph.facebook.com port 443: Bad access
INFO - Matched route "hwi_oauth_service_redirect".
CRITICAL -
Uncaught PHP Exception HWI\Bundle\OAuthBundle\OAuth\Exception\HttpTransportException:
"Error while sending HTTP request"
at C:\myProject\vendor\hwi\oauth-bundle\OAuth\ResourceOwner\AbstractResourceOwner.php
line 257
DEBUG -
Notified event "kernel.request" to listener
"Symfony\Component\EventDispatcher\Debug\WrappedListener::__invoke".
...
I'm now looking for help about this. Or any other way to get those bundles to work together.
config.yml :
hwi_oauth:
# list of names of the firewalls in which this bundle is active, this setting MUST be set
firewall_names: [auth]
http_client:
timeout: 10000
verify_peer: false
max_redirects: 1000
ignore_errors: false
fosub:
username_iterations: 300
properties:
# these properties will be used/redefined later in the custom FOSUBUserProvider service.
facebook: facebook_id
# an optional setting to configure a query string parameter which can be used to redirect
# the user after authentication, e.g. /connect/facebook?_destination=/my/destination will
# redirect the user to /my/destination after facebook authenticates them. If this is not
# set then the user will be redirected to the original resource that they requested, or
# the base address if no resource was requested. This is similar to the behaviour of
# [target_path_parameter for form login](http://symfony.com/doc/2.0/cookbook/security/form_login.html).
# target_path_parameter: _destination
# an optional setting to use the HTTP REFERER header to be used in case no
# previous URL was stored in the session (i.e. no resource was requested).
# This is similar to the behaviour of
# [using the referring URL for form login](http://symfony.com/doc/2.0/cookbook/security/form_login.html#using-the-referring-url).
# use_referer: true
# here you will add one (or more) configurations for resource owners
resource_owners:
facebook:
type: facebook
client_id: {id}
client_secret: {secret}
scope: ""
infos_url: "https://graph.facebook.com/me?fields=name,email,picture.type(square)"
options:
display: popup
security.yml :
firewalls:
auth:
pattern: ^/api/minisite/user/auth
anonymous: true
stateless: true
form_login:
check_path: /api/minisite/user/auth/login_check
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
username_parameter: username
password_parameter: password
require_previous_session: false
oauth:
resource_owners:
facebook: "/api/minisite/user/auth/facebook/login/check-facebook"
login_path: /api/minisite/user/auth/facebook/login
check_path: /api/minisite/user/auth/login_check
failure_path: /api/minisite/user/auth/facebook/login
oauth_user_provider:
#this is my custom user provider, created from FOSUBUserProvider - will manage the
#automatic user registration on your site, with data from the provider (facebook. google, etc.)
service: my_user_provider
logout: true
anonymous: true
access_control:
- { path: ^/api/minisite/user/auth, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api/minisite, roles: IS_AUTHENTICATED_FULLY }
Configure this in your config.yml file.
hwi_oauth:
http_client:
verify_peer: false
Setting this allows you to turn off SSL verification.
I got the same problem using HWI on localhost. I don't know, but if it's your case, try to upload your work on a server. The reason of this issue is that your are using the port 80, but facebook need that you use the port 443 or use ipv6.
Hope this help

How to exclude an api route from symfony2 firewall based on method

So i am building a symfony2 api using fosrestbundle fosuserbundle and LexikJWTAuthenticationBundle and when i want to acces to /api/users.json to post a new user i get a 401 error Bad Credentials.
i tried to add a line in access control this way :
- { path: post_user, role: IS_AUTHENTICATED_ANONYMOUSLY }
but it didn't work.
i also tried :
- { path: post_user, role: IS_AUTHENTICATED_ANONYMOUSLY, methods:[POST] }
how can i exclude only the post endpoint ?
The solution is to create a new firewall disabling authentication on a url pattern. The tricky thing is that security configuration also allows you to select the methods covered by the firewall.
Just add this in your firewalls in security.yml :
public:
methods: [POST]
pattern: ^/api/users
security: false
you have now access to your endpoint on post method and get put and delete will still require whatever authentication protocol you use :)
Do mind when using Adel's solution and using #Security Annotations in your controller or actions you get this exception :
The token storage contains no authentication token. One possible reason may be that there is no firewall configured for this URL.
This can be circumvented by replacing security: false with anonymous : true. So the complete solution is :
public:
methods: [POST]
pattern: ^/api/users
anonymous : true