Chapters
37 Chapters
|
3:43:31
|
Login to bookmark this video
-
Course Code
Subscribe to download the code!
Subscribe to download the code!
-
This Video
Subscribe to download the video!
Subscribe to download the video!
-
Subtitles
Subscribe to download the subtitles!
Subscribe to download the subtitles!
-
Course Script
Subscribe to download the script!
Subscribe to download the script!
Scroll down to the script below, click on any sentence (including terminal blocks) to jump to that spot in the video!
Subscribe to jump to this part in the video!
What PHP libraries does this tutorial use?
// composer.json
{
"require": {
"php": ">=8.2",
"ext-ctype": "*",
"ext-iconv": "*",
"api-platform/core": "^3.0", // v3.1.2
"doctrine/annotations": "^2.0", // 2.0.1
"doctrine/doctrine-bundle": "^2.8", // 2.8.3
"doctrine/doctrine-migrations-bundle": "^3.2", // 3.2.2
"doctrine/orm": "^2.14", // 2.14.1
"nelmio/cors-bundle": "^2.2", // 2.2.0
"nesbot/carbon": "^2.64", // 2.66.0
"phpdocumentor/reflection-docblock": "^5.3", // 5.3.0
"phpstan/phpdoc-parser": "^1.15", // 1.16.1
"symfony/asset": "6.2.*", // v6.2.5
"symfony/console": "6.2.*", // v6.2.5
"symfony/dotenv": "6.2.*", // v6.2.5
"symfony/expression-language": "6.2.*", // v6.2.5
"symfony/flex": "^2", // v2.2.4
"symfony/framework-bundle": "6.2.*", // v6.2.5
"symfony/property-access": "6.2.*", // v6.2.5
"symfony/property-info": "6.2.*", // v6.2.5
"symfony/runtime": "6.2.*", // v6.2.5
"symfony/security-bundle": "6.2.*", // v6.2.6
"symfony/serializer": "6.2.*", // v6.2.5
"symfony/twig-bundle": "6.2.*", // v6.2.5
"symfony/ux-react": "^2.6", // v2.7.1
"symfony/ux-vue": "^2.7", // v2.7.1
"symfony/validator": "6.2.*", // v6.2.5
"symfony/webpack-encore-bundle": "^1.16", // v1.16.1
"symfony/yaml": "6.2.*" // v6.2.5
},
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "^3.4", // 3.4.2
"mtdowling/jmespath.php": "^2.6", // 2.6.1
"phpunit/phpunit": "^9.5", // 9.6.3
"symfony/browser-kit": "6.2.*", // v6.2.5
"symfony/css-selector": "6.2.*", // v6.2.5
"symfony/debug-bundle": "6.2.*", // v6.2.5
"symfony/maker-bundle": "^1.48", // v1.48.0
"symfony/monolog-bundle": "^3.0", // v3.8.0
"symfony/phpunit-bridge": "^6.2", // v6.2.5
"symfony/stopwatch": "6.2.*", // v6.2.5
"symfony/web-profiler-bundle": "6.2.*", // v6.2.5
"zenstruck/browser": "^1.2", // v1.2.0
"zenstruck/foundry": "^1.26" // v1.28.0
}
}
9 Comments
Hi, it's me again. Asking questions here and there 😅. I am not sure if this is the correct topic for my question, but its about authorization, so:
We are building an app where the user can work on different projects, so depending on the selected project, we automatically inject the right project IRI to every request as a filter. There is also a automatic condition in my backend repository like
project in (:allValid)for item and collectionThis works fine, but we need the user to have different roles depending on the project. We have a
ProjectUserentity with project, user and roles and I plan to somehow inject the roles on authentication into my user and then everything afterwards works like before (iE Security voters)But this implies that I need exactly one project (so no more filter for the current project) because I need to find exactly one
ProjectUserso I am thinking about saving the current project in the session (will be set on login and by custom route to switch).I know how to save it into the session but not how to inject the roles into my user depending on the selected project.
Is this the right approach?
Hey @Sebastian-K!
Hmmm, very interesting situation! Ok, so typically (as you know), we read the roles from the User object. This means that, once the user logs in, the roles are static throughout the rest of that user's session. Your situation is a bit different, if I understand it, because the user could switch the project, which would change the roles for that user. There are two approaches I can think of, and I'm not positive which will be better :)
A) Try to "change" the roles on the user when the project is selected/changed. In reality, when the user authenticates, the "roles" are stored on the "token". The roles for the token are populated from
User::getRoles()(https://github.com/symfony/symfony/blob/6.4/src/Symfony/Component/Security/Http/Authenticator/AbstractAuthenticator.php#L31) but ultimately, the final roles live on the token - and that's what matters for authorization.Specifically, the
RoleVotercalls$token->getRoleNames()during authorization to figure out what roles the currently-authenticated user has - https://github.com/symfony/symfony/blob/6.4/src/Symfony/Component/Security/Core/Authorization/Voter/RoleVoter.php#L66So, in theory, you could create your own, custom token class (that likely extends
AbstractToken) and give this a method likesetRoles()where you can override the current roles. To allow this to be used, you would need to extend / hook into whatever your authentication "authenticator" is and create your custom token object instead of the default one: https://github.com/symfony/symfony/blob/6.4/src/Symfony/Component/Security/Http/Authenticator/AbstractAuthenticator.php#L29-L32Then, when the user changes projects, you could set the new roles onto your token using that
->setRoles()method. Since it sounds like you're using session-based authentication, the updated token with the updated roles would get serialized into the session at the end of that request and used on the future requests.B) The other approach would be to do nothing special with the token or trying to make the roles dynamic. In this case, instead of protecting your app with roles like
ROLE_EDITOR, you would create some new prefix for project-related roles and use things likePROJECT_EDITOR. You would then create a custom voter for this, which would look at the current project and see if the user has this "role". This solution is a bit simpler.Cheers!
Hello Ryan, another little typo error in your code here :
or someone invented a new protocole: thhp i didn't know ? ;) ... many thanks for tutos !
Lol - thank you @Pierre-A
Hello Ryan,
Thanks for this tutorial.
Like Ugo, I am also a bit confused but by another another point.
When you say "Or imagine an iPhone app where, to log in, you show the user the login form on your site. Then, via an OAuth flow, that mobile app will receive an access token it can use to talk to your API on behalf of that user."
Why do we need OAuth here ?
They state the same thing on the link you gave (auth0.net) : "If the Application is a native app, then use the Authorization Code Flow with Proof Key for Code Exchange (PKCE)."
I was thinking it was possible to just use JWT to authenticate for a native app. For example, let's take Flutter, we can use the secure storage (https://pub.dev/packages/flutter_secure_storage) to store the JWT, right ? So is there something wrong with sending my email+password to my api endpoint (like in normal spa), then read the answer, store the token in the safe place and then re-use for latter requests by adding this token to the Authorization header (i saw this implementation in few places also). I can also use this token, to display connected / not connected page depending if token is valid or not (by checking from time to time).
But since it's seem mandatory to use Oauth with PKCE, i think i'm missing something about the security but i don't see what :-)
If you can help on this subject, it would be very nice.
Thanks a lot.
Hey @Romain-L!
Sorry for the slow reply - just got back from vacation :).
Yea.. this stuff is SO tricky - and I'm not an expert on every aspect for sure!
Yes, I'm 99% sure that this will "work". And, about storage, I don't know what the normal solution is for this, but even with OAuth, after you complete the process, you'll need to store the access token somewhere. So storing an access token or JWT is a legitimate thing to do.
I'm not an expert here, but yes, I think this is "fine". What you're seeing is that OAuth is recommended because it's a standard, so there's less risk that you mess something up. Additionally, with OAuth, the user ultimately enters their email/password into a browser with your site's URL in the address bar. With your solution, you enter it directly into the app... which can be a risk (assuming your users are smart enough to notice) because a "bad user" could create an app that looks like your app with a login form... which then sends their credentials somewhere else. So by using OAuth, you can increase the user's trust. But in reality, I'm not sure how many users will actually do this.
Here's another document from Auth0 that talks in more depth about this topic - https://auth0.com/docs/get-started/authentication-and-authorization-flow/mobile-device-login-flow-best-practices
Cheers!
Hello Ryan,
Thank you very much for your time and your answer. It's more clear to me know. So i will go with the "basic" solution since it's seems to be ok. I checked a few mobile apps and they all allow to enter email/pwd in the app without oauth for there own users, so let's go for this!
Thanks and have a nice day
Hi Ryan,
I am getting a bit confused..
I am developing a backoffice+api in Symfony and a front end app in vuejs/nuxt. The front end app will be used only by external users that will have to login to get their own data (authentication + authorisation). These two apps have separate repo and I was planning to use two different subdomains (ex: backoffice.myurl.com and myurl.com) and sessions. I am now getting confused with what you explained about CORS. Is anything wrong with my setup ?
thx for your help
Hi Ugo!
Excellent question! Because you're using sessions, there are 2 things to think about:
1) Can your frontend and backend share a session? The answer is yes. And it will likely happen automatically. When you send a POST request to myurl.com (your API) to authenticate, and then your API creates a session, it will probably create the SameSite session cookie under the domain myurl.com. Because backoffice.myurl.com is a subdomain of that, it can use that cookie. So, all good here ✅
2) Can your frontend JavaScript make Ajax requests to your API? The answer is "yes", "but". You WILL be able to do this. However, your frontend will first make a CORS preflight request basically asking your API if it's ok if backoffice.myurl.com makes Ajax calls to it. So, to get this to work, you'll need to configure some CORS config on your API to say that requests coming from backoffice.myurl.com are "allowed". But, you'll STILL have s flight performance impact because your frontend will first need to make that extra "preflight" request before it makes the real AJAX request. I'm not an expert on CORS, but it looks like you can set a header - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Max-Age - so that this preflight request is not made often.
So, CORS is the problem. Your setup will work, but you'll need some CORS config and you'll suffer (slightly) from that preflight request. The alternative, which Kévin Dunglas talks about - https://dunglas.dev/2022/01/preventing-cors-preflight-requests-using-content-negotiation/ - is to just use something like myurl.com/backoffice and then configure your webserver to serve the /backoffice URL from the totally different frontend app code :).
Cheers!
"Houston: no signs of life"
Start the conversation!