Symfony API Rest login and register - fosuserbundle

I'm finishing developing a API rest with common endpoints using FOSUserBundle, FOSResBundle and FOSoAuthBundle.
In a parallel way, I'm developing another symfony app to consume the services but I'm having several doubts about how to register and login users via API endpoints. My idea is to have 2 controllers refered it's routes (such as /login and /register) and do this actions via api endpoint.
with FOSUserbundle this actions are simple but don't know how to 'consume' trough APIRest service.

I know it's only half an answer but I currently do this with login, but not registration (I don't need it).
I am using Symfony 4 and flex, but it should work on 3.4 I think. I am also using json.
Bundles
Bundles I use for this (on top of fosuser etc):
jms/serializer-bundle
lexik/jwt-authentication-bundle
friendsofsymfony/rest-bundle
Config
Fos Rest
fos_rest:
body_listener: true
param_fetcher_listener: force
format_listener:
enabled: true
rules:
- { path: ^/, priorities: [ json ], fallback_format: json, prefer_extension: true }
view:
view_response_listener: 'force'
formats:
json: true
xml: false
rss: false
mime_types:
json: ['application/json', 'application/x-json']
routing_loader:
default_format: json
include_format: false
exception:
enabled: true
Fos User
fos_user:
db_driver: orm
firewall_name: api
user_class: App\Entity\User
from_email:
address: admin#example.com
sender_name: Admin
JMS Serializer
jms_serializer:
visitors:
xml:
format_output: '%kernel.debug%'
Lexik JWT
You need you generate tokens and put their location in your .env file. More info and a guide here.
lexik_jwt_authentication:
private_key_path: '%kernel.project_dir%/%env(JWT_PRIVATE_KEY_PATH)%'
public_key_path: '%kernel.project_dir%/%env(JWT_PUBLIC_KEY_PATH)%'
pass_phrase: '%env(JWT_PASSPHRASE)%'
token_ttl: 3600 #whatever you like
token_extractors:
authorization_header: ~
cookie: ~
query_parameter: ~
Security
security:
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
encoders:
FOS\UserBundle\Model\UserInterface: sha512 #probably use bcrypt
providers:
fos_userbundle:
id: fos_user.user_provider.username_email
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: ROLE_ADMIN
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
api_login:
pattern: ^/login
stateless: true
anonymous: true
form_login:
check_path: /login
require_previous_session: false
username_parameter: username
password_parameter: password
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
api:
pattern: ^/
stateless: true
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
access_control:
- { path: ^/, role: IS_AUTHENTICATED_FULLY }
Routes
login:
type: rest
resource: App\Controller\LoginController
Controller
I made a loginController that is really just a placeholder to override the route
<?php
namespace App\Controller;
use FOS\RestBundle\Controller\Annotations;
use FOS\RestBundle\Controller\FOSRestController;
use FOS\RestBundle\Routing\ClassResourceInterface;
use FOS\RestBundle\Controller\Annotations\RouteResource;
/**
* #RouteResource("login", pluralize=false)
*/
class LoginController extends FOSRestController implements ClassResourceInterface
{
public function postAction()
{
throw new \DomainException('You should never see this');
}
}
Testing it
You can fire a POST request to /login and it should return a token!
{
"username": "my_user",
"password": "passw0rd"
}
Registration
I haven't actually done this but I have a simple enough idea...
Open a route to registration in your security.yaml
security:
firewalls:
api_register:
pattern: ^/register
anonymous: true
access_control:
- { path: ^/register$, role: IS_AUTHENTICATED_ANONYMOUSLY }
Register the route
registration:
type: rest
resource: App\Controller\RegistrationController
Write the controller method
Get the request data
User the FosUserManager to create a new user
- https://symfony.com/doc/current/bundles/FOSUserBundle/user_manager.html

Related

Artillery.io Login and capture response and cookies before each VU runs

Using Artillery.io load tester I am trying to login each VU Before running the Scenarios flow (I do not want the login requests to be part of the scenario results)
I tried moving the login flow to the beforeScenario but that doesn't seem to run
config:
target: '{{ $processEnvironment.URL }}'
scenarios:
- name: 'Warm Up'
beforeScenario:
flow:
- post:
url: '/login'
json:
username: 'admin'
password: 'password'
capture:
- json: '$.user._id'
as: 'userId'
flow:
- post:
url: '/api/graphql'
json:
- operationName: 'apiRequest'
query: 'query aQuery($userId: ID!) { aQuery: testQuery(userId: $userId) { id name } }'
variables:
userId: '{{ userId }}'
Is there any way to achieve this?
Not sure if you have figured this out but i used the "before" section that comes before the scenario and calls a function to helper function that login
and saves the access_token
before:
flow:
- function: "login"
scenarios:
- name: "Create"
- flow:
- post:
url: ""
headers:
Authorization: "Bearer {{access_token}}"
Before your "scenarios" section, you must use something like this (just an example):
before:
flow:
- log: "Running the Authorization before virtual users run"
- post:
url: "/users/login"
json:
username: "{{user}}"
password: "{{password}}"
capture:
json: "$.data.token"
as: "accessToken"
strict: false
expect:
- statusCode: 200

On behalf of flow returns AADSTS50013 Assertion failed signature validation. Reason - The key was not found

I have a Web API that needs to make calls to Microsoft Graph API on behalf of the user. I'm using the OBO flow as described here.
To get the access token, I'm posting to
POST - https://login.microsoftonline.com/<my-tenant-id>/oauth2/v2.0/token
grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
&client_id=<my-client-id>
&client_secret=<my-client-secret>
&assertion=<user's access token>
&scope=https://graph.microsoft.com/files.readwrite.all
&requested_token_use=on_behalf_of
The call fails with the following message:
{
"error": "invalid_grant",
"error_description": "AADSTS50013: Assertion failed signature validation. [Reason - The key was not found.]\r\nTrace ID: fb813f8e-ce15-4d09-bbae-9db5e5195a00\r\nCorrelation ID: ecb4e087-5506-4224-8f11-72fbf574beac\r\nTimestamp: 2020-10-22 02:18:57Z",
"error_codes": [
50013
],
"timestamp": "2020-10-22 02:18:57Z",
"trace_id": "fb813f8e-ce15-4d09-bbae-9db5e5195a00",
"correlation_id": "ecb4e087-5506-4224-8f11-72fbf574beac",
"error_uri": "https://login.microsoftonline.com/error?code=50013"
}
Any ideas what I could be doing wrong?
In response to #Chauncy Zhou,
I have an Angular app with:
"#azure/msal-angular": "^1.0.0",
"msal": "1.3.3"
In app.module, MSAL is configured something like:
MsalModule.forRoot(
{
auth: {
clientId: environment.msClientId, // app1-client-id
authority: environment.msAuthorithy, // https://login.microsoftonline.com/<tenant-id>
redirectUri: environment.msRedirectUri // https://localhost:4200/callback
},
cache: {
cacheLocation: 'localStorage',
storeAuthStateInCookie: false
},
},
{
consentScopes: ['user.read', 'openid'],
protectedResourceMap: [
['https://app-2', ['api://app-2-id/scope-name']],
]
}
)
In a test component:
import { Component, OnInit } from '#angular/core';
import { MsalService } from '#azure/msal-angular';
#Component({
selector: 'app-test',
templateUrl: './test.component.html',
styleUrls: ['./test.component.scss']
})
export class TestComponent implements OnInit {
constructor(private msal: MsalService) {}
ngOnInit(): void {
this.msal.acquireTokenSilent({ scopes: ['api://app-2/scope-name'] }).then(response => {
console.log(response);
});
}
}
Take the access token from console, and do a POST for OBO in postman.
The error is &assertion.
You need to create two application,one is client application(test_1) and the other is web api application(test_2).
In the test_2 application,you need to expose an api.
And in the test_1 application,you need to give test_1 permission to access test_2.
The testing session.
First, use the ROPC grant flow to request access token for test_ 2 application
ropc grant flow
Note that the scope here is api://{test_2 application id}/.default.
Second,use OBO flow to request access token for microsoft graph api endpoint.
on-behalf-of flow

How to retrieve user info with Azure AD scopes and oidc-client.js?

I'm confused how I can get access tokens and user info details when using azure ad scopes with oidc-client.js.
I have the following scope against my app in the portal...
I then have my user manager settings set up as follows....
var settings: UserManagerSettings = {
authority: `https://login.microsoftonline.com/${tenantId}`,
client_id: clientId,
redirect_uri: "http://localhost:3000/authcallback",
post_logout_redirect_uri: "http://localhost:3000/authcallback",
response_type: "token id_token",
scope: `api://${clientId}/access_user_data openid`,
popup_redirect_uri: "http://localhost:3000/authcallback",
silent_redirect_uri: "http://localhost:3000/authcallback",
automaticSilentRenew: true,
loadUserInfo: true,
metadata: {
userinfo_endpoint: "https://graph.microsoft.com/oidc/userinfo",
authorization_endpoint: `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/authorize`,
issuer: `https://login.microsoftonline.com/${tenantId}/v2.0`,
jwks_uri: `https://login.microsoftonline.com/${tenantId}/discovery/v2.0/keys`
}
};
When I login with signinRedirect I get an access_token returned to my callback, however the call to https://graph.microsoft.com/oidc/userinfo fails with unauthorized when doing getUser().
oidc-client.min.js:1 GET https://graph.microsoft.com/oidc/userinfo 401 (Unauthorized)
The access token does appear to work with my api that requires the api://${clientId}/access_user_data scope.
The discovery document here lists the following available scopes
"scopes_supported": [
"openid",
"profile",
"email",
"offline_access"
]
Which I thought would have worked as I am also including the openid scope. Note that if I only have the openid scope like so scope: "openid", getUser() works, however it doesn't have the scope I need for calling my api.
What am I doing wrong here?
Thanks,
Had the Same issue after some research tried setting loadUserInfo to false that resolved my problem can please try the same

add admin path to symfoy back end with React js

I'm currently working on symfony 4 project with React js to handle admin panel .
I used Webpack Encore to connect React JS .
the path of The React JS app is :
my_project_symfony/
And i have an api in :
my_project_symfony/api
The React JS is supposed to present the admin interface so it must be protected by admin role .
My security config is :
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
login:
pattern: ^/api/login
stateless: true
anonymous: true
json_login:
check_path: /api/login_check
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
api:
pattern: ^/api
stateless: true
anonymous: true
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
main:
anonymous: true
refresh:
pattern: ^/token/refresh
stateless: true
anonymous: true
With this configuration how can i protect my React JS admin interface with admin role and at the same time allow role user to use my api ?
I want to :
my_project_symfony/ReactJsAPP // Protected by admin role
my_project_symfony/api// Protected by user role

HWIOAuth Symfony 4: A service definition must be an array or a string starting with "#" but string found for servic

I'm trying to integrate the FOS with HWIO on Symfony 4. Unfortunately, I ran into an error as seen below:
In FileLoader.php line 168:
A service definition must be an array or a string starting with "#" but string found for service "class" in D:\web-projects\pcrmanagement\config/services.yaml. Check your YAML syntax in D:\web-projects\pcrmanagement\config/services.yaml (which is loaded in resource "D:\web-projects\pcrmanagement\config/services.yaml").
In YamlFileLoader.php line 336:
A service definition must be an array or a string starting with "#" but string found for service "class" in D:\web-projects\pcrmanagement\config/services.yaml. Check your YAML syntax.
Here are my code below, I'll go and post only what you need to see, if you need more code to review, I can put more.
#config/hwi_oauth.yaml
hwi_oauth:
firewall_names: [main]
fosub:
username_iterations: 30
properties:
googleplus: username
resource_owners:
google:
type: google
client_id: secret
client_secret: secret
scope: "email profile"
options:
csrf: true
# security.yaml
providers:
in_memory: { memory: ~ }
fos_userbundle:
id: fos_user.user_provider.username
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
pattern: ^/
form_login:
provider: fos_userbundle
csrf_token_generator: security.csrf.token_manager
oauth:
resource_owners:
facebook: "/login/check-facebook"
google: "/login/check-google"
my_custom_provider: "/login/check-custom"
my_github: "/login/check-github"
login_path: /login
use_forward: false
failure_path: /login
oauth_user_provider:
service: my.oauth_aware.user_provider.service
logout: true
anonymous: true
#services.yaml
my.oauth_aware.user_provider.service:
class: HWI\Bundle\OAuthBundle\Security\Core\User\FOSUBUserProvider
arguments: ['#fos_user.user_manager',{google: username}]
I think there is a problem on how he reads the arguments in services.yaml
Create your custom user_provider and extend HWI\Bundle\OAuthBundle\Security\Core\User\FOSUBUserProvider.
Then config your custom provider:
services:
App\Security\Core\User\MyFOSUBUserProvider:
arguments: ['#fos_user.user_manager', { facebook: facebook_id }]
Then change:
oauth_user_provider:
service: MyBundle\Security\Core\User\MyFOSUBUserProvider

Resources