Skip to content

Commit

Permalink
Merge pull request #947 from ITfoxtec/test
Browse files Browse the repository at this point in the history
Test
  • Loading branch information
Revsgaard authored Sep 18, 2024
2 parents fb38649 + e8156dd commit a1f693c
Show file tree
Hide file tree
Showing 157 changed files with 2,774 additions and 826 deletions.
2 changes: 1 addition & 1 deletion docs/_sidebar.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
- [OpenID Connect](oidc.md)
- [OAuth 2.0](oauth-2.0.md)
- [SAML 2.0](saml-2.0.md)
- [External Login](external-login.md)
- [External API Login](external-login.md)
- [Environment Link](howto-environmentlink-foxids.md)
- [Token exchange](token-exchange.md)
- [Customization](customization.md)
Expand Down
9 changes: 3 additions & 6 deletions docs/custom-domain.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,12 @@ Custom domains is not supported in the master tenant and master environments.
## FoxIDs Cloud
Configuring a custom domain in your FoxIDs cloud tenant.

> Only sub domains is supported as custom domains, like e.g., `id.some-domain.com`, `auth.some-domain.com`, `login.some-domain.com` or `id.zyx.some-domain.com`
Steps:

1. In your DNS, add a CNAME with your custom domain and the target `custom-domains.foxids.com`
2. Configure your custom domain in your FoxIDs tenants master environment.
3. Write an email to [FoxIDs support (support@foxids.com)](mailto:support@foxids.com) and ask for a custom domain verification.
4. FoxIDs support will ask you to add one or two TXT records to your DNS for verification.
5. After successfully verification your domain become active.
2. Optionally configure your custom domain in your FoxIDs tenants master environment (otherwise configured by FoxIDs support).
3. Write an email to [FoxIDs support (support@foxids.com)](mailto:support@foxids.com) and ask us to add your custom domain.
4. After successfully verification your domain become active.

## Your own private cloud
Custom domains can be configured on tenants in the master tenant using the [Control Client](control.md#foxids-control-client) and [Control API](control.md#foxids-control-api).
Expand Down
34 changes: 24 additions & 10 deletions docs/external-login.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# External Login
# External API Login

With external login you can authenticate users in your existing user database with an [API call](#api). You implement the API which is called with a username and password, and the API then validate the username and password combination and return a response indicating success or failure.
You would use an external login authentication method if you have an existing user store to leverage the user store as a possible authentication method in FoxIDs.
After login it is possible to create [external users](users.md#external-users) and optionally show a dialog where the user e.g., can put in a name, email etc.
After login it is possible to create [external users](users.md#external-users) and optionally show a dialog where the user e.g., can put in a name, email.

> If desired, you can possible over time migrate users to FoxIDs and phase out the API and existing user store. However, it requires that emails have been added to the users.
Expand Down Expand Up @@ -37,6 +37,8 @@ The API call is secured with [HTTP Basic authentication scheme](https://datatrac

The API is called with HTTP POST and a JSON body.

> It is possible to configure additional parameters which is included in the JSON message.
This is a JSON body for the username type `email`:
```JSON
{
Expand All @@ -61,7 +63,7 @@ The username types:

### Response
**Success**
On success the API should return HTTP code 200 and optionally a list of claims for the authenticated user.
On success the API should return HTTP code 200 and optionally a list of `claims` for the authenticated user.

For example, the user's, sub (unique ID / username), name, email and maybe even a role:
```JSON
Expand Down Expand Up @@ -92,19 +94,31 @@ For example, the user's, sub (unique ID / username), name, email and maybe even
```

**Error**
The API must return HTTP code 401 (Unauthorized) if the Basic authentication is rejected.
The API must return HTTP code 401 (Unauthorized) and an `error` (required) if the Basic authentication is rejected. Optionally add an error description in `errorDescription`.
```JSON
{
"error": "invalid_api_id_secret",
"errorDescription": "Invalid API ID or secret"
}
```

The API must return HTTP code 403 (Forbidden) if the username and password combination is rejected.
The API must return HTTP code 400, 401 or 403 and an `error` (required) if the username and password combination is rejected. Optionally add an error description in `errorDescription`.
```JSON
{
"error": "invalid_username_password",
"errorDescription": "Invalid username or password."
}
```

If other errors occur, the API should return HTTP code 400 (Bad Request), 500 (Internal Server Error) or another or another appropriate error code.
If other errors occur, the API should return HTTP code 500 or another appropriate error code.
It is recommended to add a technical error message in to the return body. The error message can then later be found in the FoxIDs logs.

In all error cases, a technical error message should be added to the return body. The error message can then later be found in the FoxIDs logs.
The error message is NOT displayed for the user.
> Error messages returned from the API is NOT displayed for the user.
## API Sample
The sample [ExternalLoginApiSample](https://github.com/ITfoxtec/FoxIDs.Samples/tree/main/src/ExternalLoginApiSample) show how to implement the API in ASP.NET Core 8.
The sample [ExternalLoginApiSample](https://github.com/ITfoxtec/FoxIDs.Samples/tree/main/src/ExternalApiLoginSample) show how to implement the API in ASP.NET Core 8.

You can user this [Postman collection](https://github.com/ITfoxtec/FoxIDs.Samples/tree/main/src/ExternalLoginApiSample/external-login-api.postman_collection.json) to call and test the sample with [Postman](https://www.postman.com/downloads/).
You can user this [Postman collection](https://github.com/ITfoxtec/FoxIDs.Samples/tree/main/src/ExternalLoginApiSample/external-api-login.postman_collection.json) to call and test the sample with [Postman](https://www.postman.com/downloads/).

## Configure
Configure a external login authentication method to call your API in [FoxIDs Control Client](control.md#foxids-control-client).
Expand Down
8 changes: 4 additions & 4 deletions docs/samples.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ You can alternatively configure the samples in [your one FoxIDs environment](#co

- [AspNetCoreSamlIdPSample](#aspnetcoresamlidpsample) ([online](https://aspnetcoresamlidpsample.itfoxtec.com/))

- ASP.NET Core - External login API - authentication method
- ASP.NET Core - External API login - authentication method

- [ExternalLoginApiSample](#externalloginapisample)
- [ExternalApiLoginSample](#externalapiloginsample)

> You can use the [JWT tool](https://www.foxids.com/tools/Jwt) and [SAML 2.0 tool](https://www.foxids.com/tools/Saml) to decode tokens and create self-signed certificates with the [certificate tool](https://www.foxids.com/tools/Certificate).
Expand Down Expand Up @@ -201,9 +201,9 @@ Identity Server ([code link](https://github.com/ITfoxtec/FoxIDs.Samples/tree/mai

Local development domain and port: `https://localhost:44346`

### ExternalLoginApiSample
### ExternalApiLoginSample

Sample ([code link](https://github.com/ITfoxtec/FoxIDs.Samples/tree/main/src/ExternalLoginApiSample)) application implementing an external login API which is connected as a external login authentication method in FoxIDs.
Sample ([code link](https://github.com/ITfoxtec/FoxIDs.Samples/tree/main/src/ExternalApiLoginSample)) application implementing an external API login which is connected as a external login authentication method in FoxIDs.

Local development domain and port: `https://localhost:44352`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public TDownPartyTestController(FoxIDsControlSettings settings, TelemetryScopedL
var requestDictionary = authenticationRequest.ToDictionary().AddToDictionary(codeChallengeRequest);
var testUrl = QueryHelpers.AddQueryString(UrlCombine.Combine(GetAuthority(partyName), Constants.Routes.OAuthController, Constants.Endpoints.Authorize), requestDictionary);

var mParty = new OidcDownPartyTest
var mParty = new OidcDownParty
{
Id = await DownParty.IdFormatAsync(RouteBinding, partyName),
Name = partyName,
Expand All @@ -103,7 +103,7 @@ public TDownPartyTestController(FoxIDsControlSettings settings, TelemetryScopedL
TestExpireAt = DateTimeOffset.UtcNow.AddSeconds(settings.DownPartyTestLifetime).ToUnixTimeSeconds(),
Nonce = authenticationRequest.Nonce,
CodeVerifier = codeVerifier,
AllowUpParties = testDownPartyRequest.UpPartyNames.Select(pName => new UpPartyLink { Name = pName }).ToList(),
AllowUpParties = testDownPartyRequest.UpParties.Select(p => new UpPartyLink { Name = p.Name.ToLower(), ProfileName = p.ProfileName?.ToLower() }).ToList(),
Client = new OidcDownClient
{
RedirectUris = new List<string> { authenticationRequest.RedirectUri },
Expand Down Expand Up @@ -139,7 +139,7 @@ public TDownPartyTestController(FoxIDsControlSettings settings, TelemetryScopedL
await secretHashLogic.AddSecretHashAsync(oauthClientSecret, secret);
mParty.Client.Secrets = [oauthClientSecret];

if (!await validateModelGenericPartyLogic.ValidateModelAllowUpPartiesAsync(ModelState, nameof(testDownPartyRequest.UpPartyNames), mParty)) return BadRequest(ModelState);
if (!await validateModelGenericPartyLogic.ValidateModelAllowUpPartiesAsync(ModelState, nameof(testDownPartyRequest.UpParties), mParty)) return BadRequest(ModelState);

mParty.DisplayName = $"Test application {(mParty.AllowUpParties.Count() == 1 ? $"[{GetUpPartyDisplayName(mParty.AllowUpParties.First())}]" : $"- {mParty.Name}")}";

Expand All @@ -161,8 +161,8 @@ public TDownPartyTestController(FoxIDsControlSettings settings, TelemetryScopedL
{
if (ex.StatusCode == DataStatusCode.Conflict)
{
logger.Warning(ex, $"Conflict, Create '{typeof(OidcDownPartyTest).Name}' by name '{partyName}'.");
return Conflict(typeof(OidcDownPartyTest).Name, partyName, nameof(OidcDownPartyTest.Name));
logger.Warning(ex, $"Conflict, Create '{typeof(OidcDownParty).Name}' by name '{partyName}'.");
return Conflict(typeof(OidcDownParty).Name, partyName, nameof(OidcDownParty.Name));
}
throw;
}
Expand Down Expand Up @@ -225,7 +225,7 @@ private string GetUpPartyDisplayName(UpPartyLink upParty)

try
{
var mParty = await tenantDataRepository.GetAsync<OidcDownPartyTest>(await DownParty.IdFormatAsync(RouteBinding, partyName));
var mParty = await tenantDataRepository.GetAsync<OidcDownParty>(await DownParty.IdFormatAsync(RouteBinding, partyName));

(var tokenResponse, var idTokenPrincipal, var accessTokenPrincipal) = await AcquireTokensAsync(mParty, clientSecret, mParty.Nonce, testDownPartyRequest.Code);

Expand Down Expand Up @@ -255,15 +255,15 @@ private string GetUpPartyDisplayName(UpPartyLink upParty)
}
catch (ResponseErrorException rex)
{
logger.Warning(rex, $"Response error, Update '{typeof(OidcDownPartyTest).Name}' by name '{partyName}'.");
logger.Warning(rex, $"Response error, Update '{typeof(OidcDownParty).Name}' by name '{partyName}'.");
ModelState.AddModelError(string.Empty, rex.Message);
return BadRequest(ModelState);
}
catch (FoxIDsDataException ex)
{
if (ex.StatusCode == DataStatusCode.NotFound)
{
logger.Warning(ex, $"NotFound, Update '{typeof(OidcDownPartyTest).Name}' by name '{partyName}'.");
logger.Warning(ex, $"NotFound, Update '{typeof(OidcDownParty).Name}' by name '{partyName}'.");
return NotFound("Test application was not found, it has probably expired.");
}
throw;
Expand All @@ -287,7 +287,7 @@ private string GetAuthority(string partyName, bool backendCall = false)
return UrlCombine.Combine(useBackendCall ? settings.FoxIDsBackendEndpoint : (useValidCustomDomain ? $"{HttpContext.Request.Scheme}://{routeBinding.CustomDomain}" : settings.FoxIDsEndpoint), urlItems.ToArray());
}

private async Task<(TokenResponse tokenResponse, ClaimsPrincipal idTokenPrincipal, ClaimsPrincipal accessTokenPrincipal)> AcquireTokensAsync(OidcDownPartyTest mParty, string clientSecret, string nonce, string code)
private async Task<(TokenResponse tokenResponse, ClaimsPrincipal idTokenPrincipal, ClaimsPrincipal accessTokenPrincipal)> AcquireTokensAsync(OidcDownParty mParty, string clientSecret, string nonce, string code)
{
var tokenRequest = new TokenRequest
{
Expand Down
Loading

0 comments on commit a1f693c

Please sign in to comment.