You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I'm using the plugin to implement an OAuth provider in an application that consists of two components
Frontend App
The frontend app is a single-page app (SPA) and consists of HTML/CSS/JS only. It retrieves it's data via REST calls to the backend Grails app.
Backend App
The backend Grails app has the OAuth provider plugin installed and provides data to the frontend app. There is no view code in this app and it is deployed on a separate host than the frontend app.
The Spring Security REST plugin is also installed, and a JSON Web Token is added to each API request to identify authenticated users.
Problem
The difficulty with using the plugin as-is, is that it presumes the OAuth views will be rendered on the server-side by GSPs. In my case, this is contrary to how the app has been designed to work, specifically, the login, create account, forgot password, etc. views already exist in the frontend app, and we'd obviously like to re-use these for the OAuth views, rather than reimplement them as GSPs.
Proposal
When a client requests an access token, a better workflow for us is:
If the user is not authenticated, they must first of all authenticate via our existing views
Once the user has authenticated, show an OAuth approval dialog, which is implemented as a view in the frontend app (where all the other view code exists)
If approval is granted, make an API request to the backend to generate an authorization code
When the frontend app receives the authorization code it is forwarded to the client, which can exchange it for an access token with the backend app directly.
Solution
The only change the plugin requires to support this proposal is the ability to generate an authorization code programatically. I've implemented the following service and it appears to work
importgrails.plugin.springsecurity.SpringSecurityServiceimportorg.springframework.security.oauth2.provider.AuthorizationRequestimportorg.springframework.security.oauth2.provider.OAuth2Authenticationimportorg.springframework.security.oauth2.provider.OAuth2Requestimportorg.springframework.security.oauth2.provider.OAuth2RequestFactoryimportorg.springframework.security.oauth2.provider.code.AuthorizationCodeServicesclassOAuthProviderService {
OAuth2RequestFactory oauth2RequestFactory
SpringSecurityService springSecurityService
AuthorizationCodeServices authorizationCodeServices
StringgenerateAuthorizationCode(AuthorizationCodeRequestcommand) {
// the command object should already have been validated (e.g in the controller)// by the time we get to this point Map requestParams = [
scope: command.scope,
response_type: command.responseType,
redirect_uri: command.redirectUri,
client_id: command.clientId
]
// create an object representing an approved request for an authorization code, i.e. after the user has// granted their approval via the consent dialogAuthorizationRequest approvedAuthCodeRequest = oauth2RequestFactory.createAuthorizationRequest(requestParams)
approvedAuthCodeRequest.approved =trueOAuth2Request storedOAuth2Request = oauth2RequestFactory.createOAuth2Request(approvedAuthCodeRequest)
OAuth2Authentication combinedAuth =newOAuth2Authentication(storedOAuth2Request, springSecurityService.authentication)
authorizationCodeServices.createAuthorizationCode(combinedAuth)
}
}
importgrails.validation.Validateableimportorg.example.Client@ValidateableclassAuthorizationCodeRequest {
String scope
String responseType
String redirectUri
String clientId
// these constraints perform much of the same validation logic as // org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint// which would otherwise be omittedstatic constraints = {
def getClient = {
Client.findByClientId(it, [cache: true])
}
clientId validator: {
getClient(it) !=null
}
responseType inList: ["token", "code"]
redirectUri validator: { uri, AuthorizationCodeRequestself->Client client = getClient(self.clientId)
if (client) {
uri in client.redirectUris
}
}
scope validator: { scope, AuthorizationCodeRequestself->Client client = getClient(self.clientId)
if (client) {
scope in client.scopes
}
}
}
}
Obviously this is not a complete solution, e.g. an endpoint also needs to be added that calls this service. Is this something that you'd consider adding to the plugin?
The text was updated successfully, but these errors were encountered:
(Migrated from bluesliverx/grails-spring-security-oauth2-provider#89 reported by @ghost, see that issue for more details)
Background
I'm using the plugin to implement an OAuth provider in an application that consists of two components
Frontend App
The frontend app is a single-page app (SPA) and consists of HTML/CSS/JS only. It retrieves it's data via REST calls to the backend Grails app.
Backend App
The backend Grails app has the OAuth provider plugin installed and provides data to the frontend app. There is no view code in this app and it is deployed on a separate host than the frontend app.
The Spring Security REST plugin is also installed, and a JSON Web Token is added to each API request to identify authenticated users.
Problem
The difficulty with using the plugin as-is, is that it presumes the OAuth views will be rendered on the server-side by GSPs. In my case, this is contrary to how the app has been designed to work, specifically, the login, create account, forgot password, etc. views already exist in the frontend app, and we'd obviously like to re-use these for the OAuth views, rather than reimplement them as GSPs.
Proposal
When a client requests an access token, a better workflow for us is:
Solution
The only change the plugin requires to support this proposal is the ability to generate an authorization code programatically. I've implemented the following service and it appears to work
Obviously this is not a complete solution, e.g. an endpoint also needs to be added that calls this service. Is this something that you'd consider adding to the plugin?
The text was updated successfully, but these errors were encountered: