create saml2 authentication request in backend
This commit is contained in:
parent
4c9652abc3
commit
1f666a5bf9
|
@ -5,7 +5,6 @@ import eu.eudat.logic.security.customproviders.ConfigurableProvider.entities.sam
|
|||
import eu.eudat.logic.security.validators.configurableProvider.Saml2SSOUtils;
|
||||
import eu.eudat.logic.services.ApiContext;
|
||||
import eu.eudat.models.data.helpers.responses.ResponseItem;
|
||||
import eu.eudat.models.data.security.Principal;
|
||||
import eu.eudat.types.ApiMessageCode;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
|
@ -18,7 +17,7 @@ import java.nio.charset.StandardCharsets;
|
|||
|
||||
@RestController
|
||||
@CrossOrigin
|
||||
@RequestMapping(value = {"/api/saml2/metadata"})
|
||||
@RequestMapping(value = {"/api/saml2/"})
|
||||
public class Saml2MetadataController extends BaseController {
|
||||
|
||||
private final ConfigLoader configLoader;
|
||||
|
@ -29,7 +28,7 @@ public class Saml2MetadataController extends BaseController {
|
|||
this.configLoader = configLoader;
|
||||
}
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, value = {"/{configurableProviderId}"})
|
||||
@RequestMapping(method = RequestMethod.GET, value = {"metadata/{configurableProviderId}"})
|
||||
public @ResponseBody
|
||||
ResponseEntity getMetadata(@PathVariable String configurableProviderId) {
|
||||
Saml2ConfigurableProvider saml2ConfigurableProvider = (Saml2ConfigurableProvider) this.configLoader.getConfigurableProviders().getProviders().stream()
|
||||
|
@ -55,5 +54,25 @@ public class Saml2MetadataController extends BaseController {
|
|||
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseItem<String>().status(ApiMessageCode.ERROR_MESSAGE).message("Failed to fetch metadata."));
|
||||
}
|
||||
}
|
||||
@RequestMapping(method = RequestMethod.GET, value = {"authnRequest/{configurableProviderId}"})
|
||||
public @ResponseBody
|
||||
ResponseEntity<ResponseItem<String>> getAuthnRequest(@PathVariable String configurableProviderId) {
|
||||
Saml2ConfigurableProvider saml2ConfigurableProvider = (Saml2ConfigurableProvider) this.configLoader.getConfigurableProviders().getProviders().stream()
|
||||
.filter(prov -> prov.getConfigurableLoginId().equals(configurableProviderId))
|
||||
.findFirst().orElse(null);
|
||||
if (saml2ConfigurableProvider != null) {
|
||||
try {
|
||||
String authnRequestXml = Saml2SSOUtils.getAuthnRequest(saml2ConfigurableProvider);
|
||||
return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem<String>().status(ApiMessageCode.SUCCESS_MESSAGE).message("Created").payload(authnRequestXml));
|
||||
}
|
||||
catch (Exception e) {
|
||||
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseItem<String>().status(ApiMessageCode.ERROR_MESSAGE).message("Failed to create authentication request."));
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseItem<String>().status(ApiMessageCode.ERROR_MESSAGE).message("Unknown provider."));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -794,4 +794,85 @@ public class Saml2SSOUtils {
|
|||
|
||||
}
|
||||
|
||||
public static String getAuthnRequest(Saml2ConfigurableProvider provider) throws Exception {
|
||||
|
||||
AuthnRequest authnRequest = buildAuthnRequest(provider);
|
||||
|
||||
String authnRequestXml = null;
|
||||
DocumentBuilder builder;
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
try {
|
||||
Signature signature = (Signature) buildXMLObject(Signature.DEFAULT_ELEMENT_NAME);
|
||||
if(provider.isAuthnRequestsSigned()){
|
||||
|
||||
Credential credential = getCredential(provider.getSigningCert());
|
||||
signature.setSigningCredential(credential);
|
||||
signature.setSignatureAlgorithm(SignatureConstants.ALGO_ID_SIGNATURE_DSA_SHA256);
|
||||
signature.setCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
|
||||
|
||||
X509KeyInfoGeneratorFactory keyInfoGeneratorFactory = new X509KeyInfoGeneratorFactory();
|
||||
keyInfoGeneratorFactory.setEmitEntityCertificate(true);
|
||||
KeyInfoGenerator keyInfoGenerator = keyInfoGeneratorFactory.newInstance();
|
||||
signature.setKeyInfo(keyInfoGenerator.generate(getCredential(provider.getSigningCert())));
|
||||
|
||||
authnRequest.setSignature(signature);
|
||||
}
|
||||
|
||||
builder = factory.newDocumentBuilder();
|
||||
Document document = builder.newDocument();
|
||||
Marshaller out = registry.getMarshallerFactory().getMarshaller(authnRequest);
|
||||
out.marshall(authnRequest, document);
|
||||
|
||||
if(provider.isAuthnRequestsSigned()) {
|
||||
Signer.signObject(signature);
|
||||
}
|
||||
|
||||
authnRequestXml = XmlBuilder.generateXml(document);
|
||||
|
||||
}
|
||||
catch (MarshallingException | ParserConfigurationException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
return authnRequestXml;
|
||||
|
||||
}
|
||||
|
||||
private static AuthnRequest buildAuthnRequest(Saml2ConfigurableProvider provider) throws Exception {
|
||||
AuthnRequest authnRequest = (AuthnRequest) buildXMLObject(AuthnRequest.DEFAULT_ELEMENT_NAME);
|
||||
authnRequest.setIssueInstant(Instant.now());
|
||||
authnRequest.setDestination(provider.getIdpUrl());
|
||||
switch (provider.getBinding()) {
|
||||
case "Redirect":
|
||||
authnRequest.setProtocolBinding(SAMLConstants.SAML2_REDIRECT_BINDING_URI);
|
||||
break;
|
||||
case "Artifact":
|
||||
authnRequest.setProtocolBinding(SAMLConstants.SAML2_ARTIFACT_BINDING_URI);
|
||||
break;
|
||||
case "Post":
|
||||
authnRequest.setProtocolBinding(SAMLConstants.SAML2_POST_BINDING_URI);
|
||||
break;
|
||||
}
|
||||
authnRequest.setAssertionConsumerServiceURL(provider.getAssertionConsumerServiceUrl());
|
||||
authnRequest.setID(UUID.randomUUID().toString());
|
||||
authnRequest.setIssuer(buildIssuer(provider.getSpEntityId()));
|
||||
authnRequest.setNameIDPolicy(buildNameIdPolicy());
|
||||
|
||||
return authnRequest;
|
||||
}
|
||||
|
||||
private static NameIDPolicy buildNameIdPolicy() throws Exception {
|
||||
NameIDPolicy nameIDPolicy = (NameIDPolicy) buildXMLObject(NameIDPolicy.DEFAULT_ELEMENT_NAME);
|
||||
nameIDPolicy.setAllowCreate(true);
|
||||
nameIDPolicy.setFormat(NameIDType.TRANSIENT);
|
||||
|
||||
return nameIDPolicy;
|
||||
}
|
||||
|
||||
private static Issuer buildIssuer(String spEntityId) throws Exception {
|
||||
Issuer issuer = (Issuer) buildXMLObject(Issuer.DEFAULT_ELEMENT_NAME);
|
||||
issuer.setValue(spEntityId);
|
||||
|
||||
return issuer;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,18 +1,21 @@
|
|||
import { HttpHeaders, HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Guid } from '@common/types/guid';
|
||||
import * as pk from 'pako';
|
||||
import { ConfigurationService } from './configuration/configuration.service';
|
||||
import { BaseHttpService } from './http/base-http.service';
|
||||
import { Observable } from 'rxjs';
|
||||
import { BaseComponent } from '@common/base/base.component';
|
||||
|
||||
@Injectable()
|
||||
export class SamlLoginService {
|
||||
export class SamlLoginService extends BaseComponent {
|
||||
|
||||
constructor() {}
|
||||
private actionUrl: string;
|
||||
private headers = new HttpHeaders();
|
||||
|
||||
constructor(private http: BaseHttpService, private httpClient: HttpClient, private configurationService: ConfigurationService) {
|
||||
super();
|
||||
this.actionUrl = configurationService.server + 'saml2/';
|
||||
}
|
||||
|
||||
buildRelayState(spId: string, configurableLoginId: string): string {
|
||||
let uri = 'spId=' + spId;
|
||||
uri += '&configurableLoginId=' + configurableLoginId;
|
||||
return encodeURIComponent(uri);
|
||||
}
|
||||
|
||||
resolveConfigurableLoginId(relayState: string): string {
|
||||
const decoded = decodeURIComponent(relayState);
|
||||
const routeParams = new URLSearchParams(decoded);
|
||||
|
@ -23,30 +26,9 @@ export class SamlLoginService {
|
|||
const routeParams = new URLSearchParams(decoded);
|
||||
return routeParams.has('spId') ? routeParams.get('spId') : '';
|
||||
}
|
||||
|
||||
getSamlLoginUrl(spEntityID: string, idpUrl: string, binding: string, assertionConsumerServiceUrl: string, configurableLoginId: string) {
|
||||
const now = new Date();
|
||||
let protocolBinding = '';
|
||||
switch (binding) {
|
||||
case "Redirect": protocolBinding = 'ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" '; break;
|
||||
case "Artifact": protocolBinding = 'ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" '; break;
|
||||
case "Post": protocolBinding = 'ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" '; break;
|
||||
}
|
||||
const authenticationRequest = '<saml2p:AuthnRequest xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" ID="_' + Guid.create() + '" Version="2.0" ' +
|
||||
'IssueInstant="' + now.toISOString() + '" ' +
|
||||
protocolBinding +
|
||||
'AssertionConsumerServiceUrl="' + assertionConsumerServiceUrl + '" ' +
|
||||
'Destination="' + idpUrl + '">' +
|
||||
'<saml2:Issuer>' + spEntityID + '</saml2:Issuer>' +
|
||||
'</saml2p:AuthnRequest>';
|
||||
const uint = new Uint8Array(authenticationRequest.length);
|
||||
for (let i = 0, j = authenticationRequest.length; i < j; ++i) {
|
||||
uint[i] = authenticationRequest.charCodeAt(i);
|
||||
}
|
||||
const base64String = btoa(pk.deflateRaw(uint, { to: 'string' }));
|
||||
const relayState = this.buildRelayState(spEntityID, configurableLoginId);
|
||||
const url = idpUrl + '?RelayState=' + relayState + '&SAMLRequest=' + encodeURIComponent(base64String);
|
||||
return url;
|
||||
|
||||
getAuthnRequest(configurableLoginId: string): Observable<string> {
|
||||
return this.http.get<string>(this.actionUrl + 'authnRequest/' + configurableLoginId, { headers: this.headers });
|
||||
}
|
||||
|
||||
}
|
|
@ -8,6 +8,7 @@ import { ConfigurableProvidersService } from '@app/ui/auth/login/utilities/confi
|
|||
import { LoginService } from '@app/ui/auth/login/utilities/login.service';
|
||||
import { BaseComponent } from '@common/base/base.component';
|
||||
import { environment } from 'environments/environment';
|
||||
import * as pk from 'pako';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { ConfigurationService } from '@app/core/services/configuration/configuration.service';
|
||||
import { SamlLoginService } from '@app/core/services/saml-login.service';
|
||||
|
@ -86,10 +87,28 @@ export class ConfigurableLoginComponent extends BaseComponent implements OnInit
|
|||
}
|
||||
else if(this.provider.type === ConfigurableProviderType.Saml2){
|
||||
let provider = this.provider as Saml2ConfigurableProvider;
|
||||
window.location.href = this.samlLoginService.getSamlLoginUrl(provider.spEntityId, provider.idpUrl, provider.binding, provider.assertionConsumerServiceUrl, provider.configurableLoginId);
|
||||
this.samlLoginService.getAuthnRequest(provider.configurableLoginId).pipe(takeUntil(this._destroyed))
|
||||
.subscribe(
|
||||
authenticationRequest => {
|
||||
const uint = new Uint8Array(authenticationRequest.length);
|
||||
for (let i = 0, j = authenticationRequest.length; i < j; ++i) {
|
||||
uint[i] = authenticationRequest.charCodeAt(i);
|
||||
}
|
||||
const base64String = btoa(pk.deflateRaw(uint, { to: 'string' }));
|
||||
const relayState = this.buildRelayState(provider.spEntityId, provider.configurableLoginId);
|
||||
const url = provider.idpUrl + '?RelayState=' + relayState + '&SAMLRequest=' + encodeURIComponent(base64String);
|
||||
window.location.href = url;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
buildRelayState(spId: string, configurableLoginId: string): string {
|
||||
let uri = 'spId=' + spId;
|
||||
uri += '&configurableLoginId=' + configurableLoginId;
|
||||
return encodeURIComponent(uri);
|
||||
}
|
||||
|
||||
public configurableLoginUser(code: string, state: string) {
|
||||
if (state !== (<Oauth2ConfigurableProvider>this.provider).state) {
|
||||
this.router.navigate(['/login'])
|
||||
|
|
Loading…
Reference in New Issue