Skip to content
This repository was archived by the owner on Dec 24, 2020. It is now read-only.

Commit e088204

Browse files
pierrejkevinchalet
pierrej
authored andcommitted
Update the Cordova sample to use the authorization code flow
1 parent 1395ebe commit e088204

File tree

11 files changed

+78
-28
lines changed

11 files changed

+78
-28
lines changed

samples/Cordova/Backend/Backend.xproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@
1515
<SchemaVersion>2.0</SchemaVersion>
1616
</PropertyGroup>
1717
<Import Project="$(VSToolsPath)\DotNet.Web\Microsoft.DotNet.Web.targets" Condition="'$(VSToolsPath)' != ''" />
18-
</Project>
18+
</Project>

samples/Cordova/Backend/Extensions/AppBuilderExtensions.cs

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using Microsoft.AspNetCore.Builder;
3+
using Microsoft.AspNetCore.Hosting;
34
using Microsoft.AspNetCore.Http;
45

56
namespace Backend.Extensions {

samples/Cordova/Backend/Providers/AuthorizationProvider.cs

+50-6
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@ namespace Backend.Providers {
1111
public sealed class AuthorizationProvider : OpenIdConnectServerProvider {
1212
public override async Task ValidateAuthorizationRequest(ValidateAuthorizationRequestContext context) {
1313
// Note: the OpenID Connect server middleware supports the authorization code, implicit and hybrid flows
14-
// but this authorization provider only accepts response_type=token authorization/authentication requests.
15-
if (!context.Request.IsImplicitFlow()) {
14+
// but this authorization provider only accepts response_type=code authorization/authentication requests.
15+
// You may consider relaxing it to support the implicit or hybrid flows. In this case, consider adding
16+
// checks rejecting implicit/hybrid authorization requests when the client is a confidential application.
17+
if (!context.Request.IsAuthorizationCodeFlow()) {
1618
context.Reject(
1719
error: OpenIdConnectConstants.Errors.UnsupportedResponseType,
18-
description: "Only the implicit flow is supported by this authorization server.");
20+
description: "Only the authorization code flow is supported by this authorization server.");
1921

2022
return;
2123
}
@@ -43,7 +45,7 @@ public override async Task ValidateAuthorizationRequest(ValidateAuthorizationReq
4345
if (application == null) {
4446
context.Reject(
4547
error: OpenIdConnectConstants.Errors.InvalidClient,
46-
description: "Application not found in the database: ensure that your client_id is correct");
48+
description: "Application not found in the database: ensure that your client_id is correct.");
4749

4850
return;
4951
}
@@ -52,14 +54,56 @@ public override async Task ValidateAuthorizationRequest(ValidateAuthorizationReq
5254
!string.Equals(context.RedirectUri, application.RedirectUri, StringComparison.Ordinal)) {
5355
context.Reject(
5456
error: OpenIdConnectConstants.Errors.InvalidClient,
55-
description: "Invalid redirect_uri");
57+
description: "Invalid redirect_uri.");
5658

5759
return;
5860
}
5961

6062
context.Validate(application.RedirectUri);
6163
}
6264

65+
public override async Task ValidateTokenRequest(ValidateTokenRequestContext context) {
66+
// Note: the OpenID Connect server middleware supports authorization code, refresh token, client credentials
67+
// and resource owner password credentials grant types but this authorization provider uses a safer policy
68+
// rejecting the last two ones. You may consider relaxing it to support the ROPC or client credentials grant types.
69+
if (!context.Request.IsAuthorizationCodeGrantType() && !context.Request.IsRefreshTokenGrantType()) {
70+
context.Reject(
71+
error: OpenIdConnectConstants.Errors.UnsupportedGrantType,
72+
description: "Only authorization code and refresh token grant types " +
73+
"are accepted by this authorization server.");
74+
75+
return;
76+
}
77+
78+
// Reject the request if the client identifier is missing.
79+
if (string.IsNullOrEmpty(context.ClientId)) {
80+
context.Reject(
81+
error: OpenIdConnectConstants.Errors.InvalidRequest,
82+
description: "The mandatory client_id parameter was missing");
83+
84+
return;
85+
}
86+
87+
var database = context.HttpContext.RequestServices.GetRequiredService<ApplicationContext>();
88+
89+
// Retrieve the application details corresponding to the requested client_id.
90+
var application = await (from entity in database.Applications
91+
where entity.ApplicationID == context.ClientId
92+
select entity).SingleOrDefaultAsync(context.HttpContext.RequestAborted);
93+
94+
if (application == null) {
95+
context.Reject(
96+
error: OpenIdConnectConstants.Errors.InvalidClient,
97+
description: "Application not found in the database: ensure that your client_id is correct.");
98+
99+
return;
100+
}
101+
102+
// Note: the client credentials cannot be safely stored in the Cordova application.
103+
// In this case, context.Skip() is called to inform the OIDC server that the client is not trusted.
104+
context.Skip();
105+
}
106+
63107
public override async Task ValidateLogoutRequest(ValidateLogoutRequestContext context) {
64108
var database = context.HttpContext.RequestServices.GetRequiredService<ApplicationContext>();
65109

@@ -74,7 +118,7 @@ public override async Task ValidateLogoutRequest(ValidateLogoutRequestContext co
74118
if (!await database.Applications.AnyAsync(application => application.LogoutRedirectUri == context.PostLogoutRedirectUri)) {
75119
context.Reject(
76120
error: OpenIdConnectConstants.Errors.InvalidClient,
77-
description: "Invalid post_logout_redirect_uri");
121+
description: "Invalid post_logout_redirect_uri.");
78122

79123
return;
80124
}

samples/Cordova/Backend/Startup.cs

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
using System;
2-
using System.IdentityModel.Tokens.Jwt;
3-
using System.Reflection;
1+
using System;
42
using AspNet.Security.OAuth.Validation;
53
using Backend.Extensions;
64
using Backend.Models;
@@ -80,9 +78,10 @@ public void Configure(IApplicationBuilder app) {
8078
app.UseOpenIdConnectServer(options => {
8179
options.Provider = new AuthorizationProvider();
8280

83-
// Enable the authorization and logout endpoints.
81+
// Enable the authorization, logout and token endpoints.
8482
options.AuthorizationEndpointPath = "/connect/authorize";
8583
options.LogoutEndpointPath = "/connect/logout";
84+
options.TokenEndpointPath = "/connect/token";
8685

8786
// Register a new ephemeral key, that is discarded when the application
8887
// shuts down. Tokens signed using this key are automatically invalidated.

samples/Cordova/Backend/project.json

+1-4
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,7 @@
4646

4747
"netcoreapp1.0": {
4848
"dependencies": {
49-
"Microsoft.NETCore.App": {
50-
"type": "platform",
51-
"version": "1.0.0"
52-
}
49+
"Microsoft.NETCore.App": { "type": "platform", "version": "1.0.0" }
5350
},
5451

5552
"imports": [

samples/Cordova/MobileApp/config.xml

+2-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,8 @@
104104
<platform name="wp8">
105105
<splash src="res/screens/wp8/SplashScreenImage.jpg" width="480" height="800" />
106106
</platform>
107-
<vs:plugin name="cordova-plugin-inappbrowser" version="1.1.1" />
108107
<vs:plugin name="cordova-plugin-whitelist" version="1.2.0" />
109108
<access origin="localhost:54540" />
109+
<plugin name="cordova-plugin-inappbrowser" version="1.1.1" />
110+
<access origin="127.0.0.1:80" />
110111
</widget>

samples/Cordova/MobileApp/www/index.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<head>
44
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
55
<meta charset="utf-8" />
6-
<meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com http://localhost:54540 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *">
6+
<meta http-equiv="Content-Security-Policy" content="default-src 'self' https://ssl.gstatic.com http://localhost:54540 http://192.168.11.100:54540 * data: gap: 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *">
77
<title>MobileApp</title>
88

99
<!-- Load stylesheets -->

samples/Cordova/MobileApp/www/scripts/controllers/authController.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ app.controller('authController', ['$sce', '$scope', '$location', 'authService',
55

66
$scope.logIn = function () {
77
authService.logIn().then(function (response) {
8-
authService.confirmMessage().then(function (response) { $scope.message = response });
8+
authService.confirmMessage().then(function (response) { $scope.message = response; });
99
}, function (err) {
1010
$scope.message = err;
1111
});
@@ -20,7 +20,7 @@ app.controller('authController', ['$sce', '$scope', '$location', 'authService',
2020
};
2121

2222
$scope.queryServer = function () {
23-
authService.confirmMessage().then(function (response) { $scope.message = response });
23+
authService.confirmMessage().then(function (response) { $scope.message = response; });
2424
};
2525

2626
$scope.authentication = authService.authentication;

samples/Cordova/MobileApp/www/scripts/init.js

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ app.constant('authConfig', {
44
clientId: 'myClient',
55
logInApi: 'http://localhost:54540/connect/authorize',
66
logOutApi: 'http://localhost:54540/connect/logout',
7+
tokenApi: 'http://localhost:54540/connect/token',
8+
messageApi: 'http://localhost:54540/api/message',
79
redirect_uri: 'http://localhost/callback',
810
post_logout_redirect_uri: 'http://localhost/callback'
911
});

samples/Cordova/MobileApp/www/scripts/services/authService.js

+14-8
Original file line numberDiff line numberDiff line change
@@ -9,30 +9,36 @@ app.factory('authService', ['$http', '$q', 'localStorageService', 'utilitiesServ
99
};
1010

1111
var _logIn = function () {
12-
var deferred = $q.defer()
13-
var ref = window.open(authConfig.logInApi + '?client_id=' + authConfig.clientId + '&redirect_uri=' + authConfig.redirect_uri + '&response_type=token', '_blank', 'location=no');
12+
var deferred = $q.defer();
13+
var ref = window.open(authConfig.logInApi + '?client_id=' + authConfig.clientId + '&redirect_uri=' + authConfig.redirect_uri + '&response_type=code&scope=openid', '_blank', 'location=no');
1414
ref.addEventListener('loadstart', function (event) {
1515
if (utilitiesService.startsWith(event.url, authConfig.redirect_uri)) {
16-
var requestToken = utilitiesService.getURLParameter(event.url, "access_token");
17-
var userName = jwtHelper.decodeToken(requestToken).unique_name;
16+
var authorizationCode = utilitiesService.getURLParameter(event.url, "code");
17+
$http.post(authConfig.tokenApi,
18+
'grant_type=authorization_code&code=' + authorizationCode + '&client_id=' + authConfig.clientId + '&redirect_uri=' + authConfig.redirect_uri,
19+
{ headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }).then(function successCallback(response) {
20+
var requestToken = response.data.access_token;
21+
var idToken = response.data.id_token;
22+
var userName = jwtHelper.decodeToken(idToken).unique_name;
1823
localStorageService.set('authorizationData', { token: requestToken, userName: userName });
1924
_fillAuthData();
2025
deferred.resolve(ref.close());
21-
};
26+
});
27+
}
2228
});
2329
return deferred.promise;
2430
};
2531

2632
var _logOut = function () {
27-
var deferred = $q.defer()
33+
var deferred = $q.defer();
2834
var ref = window.open(authConfig.logOutApi + '?post_logout_redirect_uri=' + authConfig.post_logout_redirect_uri, '_blank', 'location=no');
2935
ref.addEventListener('loadstart', function (event) {
3036
if (utilitiesService.startsWith(event.url, authConfig.post_logout_redirect_uri)) {
3137
localStorageService.remove('authorizationData');
3238
_authentication.isAuth = false;
3339
_authentication.userName = "";
3440
deferred.resolve(ref.close());
35-
};
41+
}
3642
});
3743
return deferred.promise;
3844
};
@@ -46,7 +52,7 @@ app.factory('authService', ['$http', '$q', 'localStorageService', 'utilitiesServ
4652
};
4753

4854
var _confirmMessage = function () {
49-
return $http.get("http://localhost:54540/api/message").then(function successCallback(response) {
55+
return $http.get(authConfig.messageApi).then(function successCallback(response) {
5056
return response.data;
5157
});
5258
};

samples/Mvc/Mvc.Server/Controllers/AuthorizationController.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public class AuthorizationController : Controller {
2222
public AuthorizationController(ApplicationContext database) {
2323
this.database = database;
2424
}
25-
25+
2626
[Authorize, HttpGet("~/connect/authorize")]
2727
public async Task<IActionResult> Authorize(CancellationToken cancellationToken) {
2828
// Note: when a fatal error occurs during the request processing, an OpenID Connect response

0 commit comments

Comments
 (0)