J'ai 3 projets 1- Javascript SPA 2- Projet API Web, 3- IdentityServer avec EF Core
J'ai commencé le débogage de l'API et d'Identity Server et j'ai réussi à obtenir le jeton jwt mais, lorsque j'essaie d'obtenir de la valeur à partir de la méthode API qui a l'attribut d'autorisation, j'obtiens une erreur:
WWW-Authenticate →Bearer error="invalid_token", error_description="The audience is invalid"
Je n'ai trouvé aucune propriété sur l'audience dans les options d'authentification. Ceci est ma configuration dans le projet API
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
ApiSecret="secret",
Authority = "http://localhost:5000",
ApiName="fso.Api",
RequireHttpsMetadata = false,
});
Et mon fichier Config.cs dans Identity
public class Config
{
public static IEnumerable<ApiResource> GetApiResources()
{
return new List<ApiResource>
{
new ApiResource()
{
Name = "fso.Api",
DisplayName = "feasion API",
Scopes =
{
new Scope("api1"),
new Scope(StandardScopes.OfflineAccess)
},
UserClaims =
{
JwtClaimTypes.Subject,
JwtClaimTypes.EmailVerified,
JwtClaimTypes.Email,
JwtClaimTypes.Name,
JwtClaimTypes.FamilyName,
JwtClaimTypes.PhoneNumber,
JwtClaimTypes.PhoneNumberVerified,
JwtClaimTypes.PreferredUserName,
JwtClaimTypes.Profile,
JwtClaimTypes.Picture,
JwtClaimTypes.Locale,
JwtClaimTypes.IdentityProvider,
JwtClaimTypes.BirthDate,
JwtClaimTypes.AuthenticationTime
}
}
};
}
public static List<IdentityResource> GetIdentityResources()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Email(),
new IdentityResources.Profile(),
};
}
// client want to access resources (aka scopes)
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientId = "fso.api",
AllowOfflineAccess=true,
ClientSecrets =
{
new Secret("secret".Sha256())
},
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
AllowedScopes =
{
StandardScopes.OfflineAccess,
"api1"
}
}
};
}
}
Voir ici pour en quoi consiste cette affirmation:
La revendication aud (audience) identifie les destinataires auxquels le JWT est destiné. Chaque mandant destiné à traiter le JWT DOIT s'identifier avec une valeur dans la revendication d'audience. Si le principal traitant la réclamation ne s'identifie pas avec une valeur dans la réclamation aud lorsque cette réclamation est présente, alors le JWT DOIT être rejeté ....
Le nom de votre API doit donc exister dans la revendication aud pour que le JWT soit valide lorsqu'il est validé par le middleware de votre API. Vous pouvez utiliser jwt.io pour regarder votre jeton en passant, cela peut être utile pour aider à le comprendre.
Pour qu'IdentityServer ajoute le nom de votre API à l'aud réclamation, votre code client (qui tente d'obtenir une ressource de l'API et a donc besoin d'un jeton d'accès) doit demander une étendue à votre API. Par exemple, comme ceci (à partir d'un client MVC):
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
Authority = Configuration["IdpAuthorityAddress"],
ClientId = "my_web_ui_id",
Scope = { "api1" },
//other properties removed...
});
Pour éviter l'erreur, l'audience doit être systématiquement ajoutée à 4 endroits
Dans mon client (par exemple MVC) en tant que portée personnalisée.
2. Dans l'application API comme ApiName
3.Dans la configuration des clients IdentityServer en tant que AllowScope
4.Dans la configuration des ressources API comme ApiResource
Voir les détails (précédemment disponibles sur le wiki IdentityServer4):
Lors de la configuration d'une nouvelle connexion API dans identityServer4, vous pouvez obtenir une erreur:
WWW-Authenticate: Bearer error="invalid_token",
error_description="The audience is invalid"
Pour éviter l'erreur, l'audience doit être systématiquement ajoutée à 4 endroits
Dans mon client (par exemple MVC) en tant que portée personnalisée:
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
Authority = Configuration["IdpAuthorityAddress"],
ClientId = "my_web_ui_id",
Scope = { "openid", "profile", "offline_access", "MyApi" },
//other properties removed for brevity...
});
Dans l'application API comme ApiName
var identityServerAuthenticationOptions = new IdentityServerAuthenticationOptions()//Microsoft.AspNetCore.Builder.IdentityServerAuthenticationOptions
{
Authority = Configuration["Authentication:IdentityServer:Authority"],
RequireHttpsMetadata = false,
EnableCaching = false,
ApiName = "MyApi",
ApiSecret = "MyApiSecret"
};
Dans IdentityServer\IdentityServerHost\Configuration\Clients.cs (ou entrée Clients correspondante dans la base de données)
var client = new Client
{
ClientId = clientId,
//other properties removed for brevity...
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
//IdentityServerConstants.StandardScopes.Email,
IdentityServerConstants.StandardScopes.OfflineAccess,
"MyApi",
},
};
Dans IdentityServer\IdentityServerHost\Configuration\Resources.cs (ou entrée ApiResource correspondante dans la base de données) en tant qu'apiResource.Scopes
var apiResource = new ApiResource
{
Name = "MyApi",
ApiSecrets =
{
new Secret("MyApiSecret".Sha256())
},
UserClaims =
{
JwtClaimTypes.Name,
JwtClaimTypes.Profile,
},
};