-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Suggestion: Add third-party oauth methods #14
Comments
I love the idea, we need to do some research to figure out how to best solve this in a way that is not adding much overhead while also solving this generically. The user manager is used for websites as well as apps as well as other services and so the solution needs to work for all of them. Initially I would say that it should be like an "extra package" that can be installed as well but does not have to be. Thoughts on that? (this is open discussion) |
In my spare time I support a Vapor Community package called Imperial. This package handles that kind of auth, but aims for towards user end auth, meaning it redirects the user from your app to the OAuth provider's authentication page and then back again. UserManager is an API for an app. There is no user interface (though one could be added). For the most part, this functionality should be handled by the app that connects to the UserManager's API. |
@proggeramlug it's hard to say... basically it's an extension of AuthController (or just another FacebookAuthController, GoogleAuthController, etc). But this should be still a part of Auth service. |
@calebkleveter, as I see Imperial does all the magic regarding the social oauth flow. it's not the part of API based service though because client sends ready-to-use social auth token to the server to process registration or sign-in. |
Trying to implement FB sign-in/sign-up... What do you think: struct FacebookData: Codable {
var name : String
var email : String
}
final class FacebookAuthController: RouteCollection {
func boot(router: Router) throws {
router.post("fbSignin", use: signin)
}
func signin(_ request: Request) throws -> Future<LoginResponse> {
let signer = try request.make(JWTService.self)
let client = try request.make(Client.self)
let fbToken = try request.content.syncGet(String.self, at: "token")
let facebookURL = "https://graph.facebook.com/me?fields=email,name,id,gender&access_token=\(fbToken)"
let facebookResponse = client.get(facebookURL)
.flatMap(to: FacebookData.self) { try $0.content.decode(FacebookData.self) }
return facebookResponse.flatMap(to: (User?, FacebookData).self) { fbUser in
return try User.query(on: request).filter(\.email == fbUser.email).first().and(result: fbUser)
}
.flatMap(to: User.self) { userInfo in
if let user = userInfo.0 {
return request.eventLoop.newSucceededFuture(result: user)
} else {
let user = try User(userInfo.1.email, "en")
user.firstname = userInfo.1.name
user.confirmed = true
user.password = try BCrypt.hash(fbToken)
return request.eventLoop.newSucceededFuture(result: user)
}
}
.flatMap(to: User.self) { user in
return user.save(on: request)
}
.flatMap(to: LoginResponse.self) { user in
let userPayload = try Payload(user: user)
let remotePayload = try request.payloadData(
signer.sign(userPayload),
with: ["userId": "\(user.requireID())"],
as: JSON.self
)
// Create a response form the access token, refresh token. and user response data.
return remotePayload.map(to: LoginResponse.self) { remotePayload in
let payload = try remotePayload.merge(userPayload.json())
let accessToken = try signer.sign(payload)
let refreshToken = try signer.sign(RefreshToken(user: user))
guard user.confirmed else { throw Abort(.badRequest, reason: "User not activated.") }
let userResponse = UserResponse(user: user, attributes: nil)
return LoginResponse(accessToken: accessToken, refreshToken: refreshToken, user: userResponse)
}
}
}
} |
I'll need to play with it some more but I think this looks pretty good already! I think we should offer this as some SPM package that can be included into the user-manager but doesn't necessarily have to. So people could configure their own flavor by just removing/adding the social media extensions they need. What do you think? @calebkleveter @dev4jam |
I like it. Just note I don't have platforms such as Facebook, so I wouldn't be able to implement those providers. |
@dev4jam If you feel comfortable with that feel free to create such a package for Facebook since you have the code already - contact me or Caleb for any support you may need. I'm thinking we should have packages such as: |
@proggeramlug Would this be server-to-server authentication? |
In parts, depending on the platform it all works generically speaking like this:
In some cases they actually do use JWTs for this or some variation of JWT. So there is some server-to-server but in all cases the user has to "manually" confirm it as well. |
This is actually my first server-side code on Vapor... So I might not know some specifics... For example, I don't really understand how to register (in the router) this controller if it will be located in a separate package. But I can try :) |
My respect for trying and being involved, that is awesome! If you just go ahead and put the controllers in a rep, I'm fairly sure we can figure out how to register them well. It probably needs to go through some middleware/service in the config. |
Add third-party OAuth methods: FB, Twitter, Google, etc
The text was updated successfully, but these errors were encountered: