Authentication.Plug - What to do as an API? I use Phoenix, but not with views. What is provided?
As I'm exploring and trying to setup
AshAuthentication
I ended up using use AshAuthentication.Plug
for a plug, thinking this would provide the same set of routes as the Phoenix view version would, but it doesn't seem like it. I'm not even sure, as the docs aren't clear on this and I'm at this point digging in the code itself.
Do I have to implement my own plug and controller(s) for this? I have an Angular frontend, and will maybe consume the same API from native apps later on.
And also, does this mean that some features aren't available unless I use Phoenix?73 Replies
You should be able to use
ash_authentication_phoenix
even if not building a UI.So I'll just not pipe it through a
:browser
pipeline and instead my :api
one, and it should work?Honestly not too sure but I know people have set it up for APIs plenty of times before
Actually, maybe not maybe people have been rolling their own sign in/\sign out endpoints
Reading the docs for the Phoenix setup it does include the
:load_from_bearer
function-plug in the example, so I assume it should be possible? I just reacted to the part where the routes were only included under a scope with :browser
, which I do not have at all.
Struggling to include the ~p
sigil from Phoenix.VerifiedRoutes
that is used in the AuthController
example of the code in the docs. I could just delete it, but it bugs me why I can't import it ๐ฎAssuming you have the generated function in your MyAppWeb file:
You would enable verified routes in your module with:
Alternatively, if you are already doing this:
you might consider just adding:
to the view macro.
This is a controller, as per the example docs of
AuthController
. I did that, but optionally. Meaning I implemented a way to not get that. And as far as I can see it should have worked as intended, but it still complains unless I inline it in the controller.
I can post my version of it later, but now I'm heading toward a train ๐I have an API only ash project successfully integrated with
ash_authentication_phoenix
. Perhaps not 100% relevant to your situation since I have a graphql api via ash_graphql
.
In the router's api pipeline I use plug(:load_from_bearer)
, followed by my own plug that takes the actor from the assigns, does some processing and then calls Ash.PlugHelpers.set_actor
.
Finally I have plug(AshGraphql.Plug)
which makes the actor available to ash_graphql
.
Following that I was able to use some of the built-in ash_authentication
actions on my user object, while I had to wrap some of them in my own actions to customise or make more compatible with my API design. Some actions such as sign_out
I had to implement myself.I also use GraphQL through AshGraphql
At the time when I implemented my GraphQL stuff I had to use my own plugs, I think? :thinkies: Or maybe I just missed that those helper functions and plugs existed haha.
This was my attempt at getting the option to include verified_routes:
Ignore any
IO
calls as they wouldn't be there normally. I just tried to debug and see what was going on as it seemingly didn't work unless I inlined it into the controller itself.
My problem is that I can't figure out what I am supposed to POST to the endpoint for password registration:
I'm getting a 401 when trying to register, as it if wants me to be authenticated to begin with before even registering. Seems odd and incorrect. My pipelines in the router for these endpoints doesn't require it and it is the AuthController's failure/3
callback that reports this.I chose a different design, where I use GQL for all of my auth. I have mutations for sign in, register and sign out etc so I never had to work out how to set up routes, other then my single
/gql
api endpoint, which was already working.Yeah, that's what I was doing before I found out about
AshAuthentication
. Had hoped to just set this up and then not think about auth for a while ๐ฅฒ I'm so early in my MVP that I don't want to think so much about the boring stuffHi @simpers if you're just doing password authentication you can submit a JSON payload that looks like
straight to the generated sign-in route (you don't need to send the confirmation field if confirmation is not enabled on your strategy). You will likely need to modify your auth controller/plug to return a JSON response rather than HTML.
The other alternative which @Alan Heywood suggested (and what I chose in another project recently) was to expose the sign in action to graphql and have the clients access every resource via the
me
graph node (ie me { posts { name } }
gives me a list of all my related posts but returns null
if there is no current user).
And have the GraphQL client take the token out of the returned payload and use it for auth. My router is set up as so:
Things get more complicated if you want to do OAuth - you will need a browser based flow for that.I want OAuth and other things, such as magic links and so on. And the confirmation was to confirm the email upon registration. I'm not at home at the moment so I'll get back to you about the details tomorrow ๐
Hi,
What did you fianlly go with @simpers. I have the same issue. I can't figure out how to add registration/sign-in/resets for api's endpoints
I'm still sitting here looking at the router. It was a national holiday in Sweden yesterday and today I've been off work so I haven't been by the computer much for two days.
The problem to me seems to be that this is meant for an OAuth2 explicit flow (not sure of the exact definition of implicit vs explicit right now tbh, it's been a while since I did this sort of thing). Though there are things in AshAuthentication to finish the remaining pieces on your own, I feel like there are some gaps in either the docs or the features to make this an explicit flow, which is what I needed. Not out of the box anyway. My frontend is Angular and not LiveView.
I am not sure how I will solve it yet as I wanted to get the other features too, so I could hook up GitHub, Google, Apple ID, e.t.c..
Happy to advise. If you implement a custom strategy you get very fine grained control over the whole process.
Is custom necessary here because the existing ones don't have what I suspected and described above?
I think I will be putting advanced auth on hold until my MVP takes a bit of shape first, and then come back to it. But it would be nice to contribute upstream if it would be possible/desired
Im not sure because Iโve never tried to do oauth without a browser-based flow and I donโt know how that works. Re custom strategy - if you canโt get what you need from the built in strategies then this is the go to method to add your own.
Doesnโt oauth make no sense without a browser?
It requires the redirect to consent screens. Pretty sure itโs literally(or at least practically) impossible.
The flow does not require a browser, as far as I'm aware. There is a difference in the flow, though. I've had to deal with Angular for a while and since it often relies on JWT rather than cookies, the flow is similar to that any native app. It's just that the flow of credentials is a little different. Thus explicit vs implicit flow. Implicit is what this is, I think.
Though I have always struggled to grasp the whole notion of this, and it was back in 2018 (gosh, time flies) when I researched this to implement some SSO stuff for some frontend(s).
For example, I think when I fiddled with Angular + Facebook Auth/data I had to get the token through some pop-up, and then pass it along to my server so the server could store it and ack to Facebook that it received it, or something like that? It's like it goes in a circle rather than the server always being in the middle.
lets back up. You have an api. You want people to be able to authenticate to use it, right?
Yup!
sorry, got alot going on today, ๐
So when people authenticate to use your api, who are they?
Okay, nvm I think I see what you're talking about
is this a way for people to give you some kind of token to access their information in some specific service?
what information are you expecting they would put in to your api to authenticate, for example
It is for for both auth and accessing others data (calendars in this case)
So sign-up can be with either pass/email as per the usual, but you should with time be able to sign-up with GitHub, Twitter, Google, AppleID, Discord whatever I might decide to add with time.
And link calendars from say Google and Apple
but when people sign up that way, they'll sign up in a browser, right?
It should not be dependent on that
why not?
If it's a native app, it'll use native APIs to accomplish this
native apps use oauth all the time, but typically they pop up a little browser window
Yes, but the UI isn't necessarily mine in these cases
For Android the UI is Google's
๐ค what does your app do?
Not much yet haha. Still in MVP. But the idea is to gather a user's calendars (work, private, whatever, e.t.c....) and use those as inputs. Then you can set a default output which your events through our service will be saved, with whatever settings are appropriate for that service (Google's settings might differ from Apple's).
Then the service will allow groups of people to find free slots for meeting up
and when will it be google's UI and not your own?
People vote on those, it is saved and it gets pushed to each person's calendar
Sign-in och registration
On say Android.
like...what app will be open when it happens ๐
On Apple it'll be a different thing, though I haven't personally implemented this.
My app will open, but the system's UI will pop up over it. Usually this is done by calling some SDK from within the app and setting up some hooks ๐
why will the system's UI pop up over it?
And that is also how I've done it in Angular previously. Used Firebase's SDK for this
Because that's how it works when integrating with them haha. What do you mean?
Like, my view will show "Sign up with any of these" and list some buttons. And depending on the click, a different SDK will be called
Then that'll take over
and that forces you to implement the implicit oauth flow on your service?
because they send you a token or something?
Yes
that sounds unideal. AFAIK implicit flows are significantly less secure than regular flows, and it means your api will have to support those
Yes
you're sure there is no way for those SDKs you're talking about to use the standard oauth flow? And open a browser window, like everyone else does everywhere else ๐ ?
Everyone else on which platforms? haha
I mean, in this case I'm talking about native apps/ios, I don't use android
can you like...do it yourself? instead of using a native SDK?
The thing is, it may or may not open a UI in a browser if it wants to (the SDK), I don't care. But how would browser cookies help in my native iOS app?
I need a JWT for the API service in that app.
Hahah well, I don't know. I'm trying to minimise the complexity of what I have to do to get my MVP up.
Since auth isn't that interesting of a problem to solve. It's been solved a million (or a billion?) times already haha
okay, so you're using the android SDK for oauth2, this thing I assume: https://developer.android.com/training/id-auth/authenticate
Android Developers
Authenticate to OAuth2 services | Android Developers
In order to securely access an online service, users need to authenticate to the serviceโthey need to provide proof of their identity. For an application that accesses a third-party service, the security problem is even more complicated. Not only doesโฆ
No, I'm not
I haven't done anything yet on the android side.
I'm only building the frontend in Angular for now, and the server in Elixir
either way, just an example. What you end up with on one end is a token valid for the target service
right?
The target service meaning my service? If so, yes
Also, I think what I want is actually OpenID Connect, which is built on top of OAuth 2.0
If I'm not mistaken.
If I'm not mistaken, what I had to do on the Facebook integration was to start the flow on the server, wait for the client to send me a nonce to pass back to Facebook to confirm the client approved the whole ordeal.
And as such the actual credential didn't go in a circle and was only visible to the server.
And they had filters and whatnot setup so that the DNS had to match with the registered IP and so on.
I'm wondering if you can just authenticate w/ google (for example) and then exchange that token w/ a regular token from your server
lol, this is @jart's wheelhouse. At the end of the day I think the unfortunate answer is that implicit oauth flows (which it seems like might be what you need) are not implemented by
ash_authentication
.
You can likely use other libraries like assent
and friends to authenticate for those cases, so ideally you don't have to be entirely on your ownI think you mean explicit, since implicit means the flow of the browser, as the browser implicitly just deals with this. Explicit is when you have to be explicit about sending the auth headers yourself.
I think assent is used by ash_authentication? :thinkies:
I'm not so sure
Yeah, it is
but you might need to use it directly instead of through ash_authentication
I don't think the standard browser based flow is the implicit flow
isn't that the "authorization code flow"?
I think a new strategy is in place.
AshAuthentication.Strategy.OIDC
or something? hahaDidn't someone get oidc working here at some point?
This is where I read it, and though it is from 2015 it is the top result haha
https://leastprivilege.com/2015/04/01/implicit-vs-explicit-authentication-in-browser-based-applications/
Dominick Baier
leastprivilege.com
Implicit vs Explicit Authentication in Browser-based Applications
I got the idea for this post from my good friend Pedro Felix โ I hope I donโt steal his thunder (I am sure I wonโt โ since he is much more elaborate than I am) โ but when I saw his tweet this morniโฆ
GitHub
assent/oidc.ex at b591e5a714765770340f6cf81e54db29118e511b ยท pow-au...
Multi-provider framework in Elixir. Contribute to pow-auth/assent development by creating an account on GitHub.
what the heck
I'm just as confused as you are. I don't know anything anymore ๐
there is an
oidc
strategy in ash_authentication
maybe that does what you want ๐AshAuthentication.UserIdentity
is part of this, I can see. It will declare a table for what I like to call connected identities.
I can't find the OIDC in the docs though, but I can see there is an issue here on discord about itI thought I had merged OIDC support
But maybe it got preempted by client work.
That could be a reason I can't find it ๐
I just linked to the docs for it
https://discord.com/channels/711271361523351632/1114147178198351902/1116474667822223380
Oh yeah. It did get merged. https://github.com/team-alembic/ash_authentication/blob/main/lib/ash_authentication/strategies/oidc.ex
GitHub
ash_authentication/oidc.ex at main ยท team-alembic/ash_authentication
The Ash Authentication framework. Contribute to team-alembic/ash_authentication development by creating an account on GitHub.
Well, it doesn't show, but I can imagine it is because the AshAuthentication framework is hidden on the left.
And I added it and tried to refresh the same link but it just reset the selection to exclude AshAuthentication

So it is there! It might need a guide then? ๐
Clarification: once I add the AshAuthentication to the browsable frameworks, I can manually search for OIDC again. But there seem to be a parameter in the URL missing to allow the linking to work @Zach Daniel
There are at least two issues open on the assent repo about mobile auth. Seems like itโs not natively supported but can be bodged.
Okay, sure! This is not the urgent part though haha. I think we got a little side-tracked tbh
The issue I am having currently is figuring out what the built-in plugs/router stuff provides for me
I'd like to just set them up and them working as expected for all strategies I add with time, but when I was trying to copy my GraphQL registration test and adapt it to AshAuthentication, I couldn't get it working :thinkies:
Which is a simple email & pass registration thing
Deleted message were jart was tagged - found the solution to that particular problem.
He is on vacation this week, FYI
may or may not get back to you until next week
Ah, thanks for letting me know! ๐ He will not be allowed to respond until he's back haha
Vacation should be vacation